I am working on an application that has a record of movies and actors. I actors to have a lot of images and/or a lot of videos which should have the user's or movie id as part of the file path. I have decided to use a m2m relationship since the image or video file could belong to either a person or a movie but I'm stuck cause I'm not sure of how to approach this problem. Here is a sample of my image class. The video class is more or less the same.
class Image(models.Model):
title = models.CharField(max_length=128)
photo = models.ImageField(upload_to=file_path)
actors = models.ManyToManyField(
'Actor',
related_name='images'
)
movies= models.ManyToManyField(
'Movie',
related_name='images',
)
But this way I have no way of storing actor or movie id as part of the file path
I thought of using a ForeignKey relationship instead of a m2m relationship but I thought that would not be accurate as not all Image objects would have an actor and Movie associated to it. I believe the relationship would be an either or relationship. Is there another way I could approach this problem?
Related
I'm currently stuck on how to do the following:
I want to track workouts and see the progress being made with the exercises.
Each workout has a name, body weight, date, and exercises.
I want to select the exercises when adding the workouts(exercises are already pre-created with a name and some tags), however, each workout is different, meaning the reps, sets, and weight for the exercise is different every time.
I'm not sure how to make a model for this.
I've added an image of a simple design I made to show what I'm after
Hope someone can help me, I'm pretty sure it's a simple solution however, my brain is stuck atm.
Image Of Design
First an foremost, I'd recommend reading through the Django models docs to get an idea of what models represent and how they work.
To address your question, I think you've correctly identified all of the information you need to create your models, so let's go through them step by step.
Architecture
It's helpful to take a step back and think about the entities in your app. Identify what they are, what attributes they posses and which of these attributes are atomic i.e. cannot be their own entity.
Workout - You mentioned that you want to track workouts with each one having name, body weight, date, and exercises. All of these attributes except for exercises seem to be atomic as they can be represented with fundamental datatypes (strings, floats, datetimes etc.). Moreover, one workout can have many exercises indicating that we need to abstract it into its own entity.
Exercise - You identified that exercises are pre-set and need to have a name and tags. A name is something we can represent with a string, however one exercise can have multiple tags, meaning it's not atomic (has a one-to-many relationship). This means we need to extract it into its own entity.
Tag - From what you said, a tag simply has one attribute which is a name that can be represented by a string. One tag can belong to many exercises.
You may be wondering where we are storing the reps, sets and weight data for each exercise in each workout. This is actually going to require an extra entity that stores the many-to-many relationship between Exercise and Workout. Let's call this Workout-Exercise.
With this information we could draw a Entity Relationship Diagram as such:
This gives us what we need to start creating Django models.
Models
Let's start with the Exercise and Tag entities. We can simply translate these directly into Django models:
from django.db import models
class Tag(models.Model):
name = models.CharField(max_length=200)
class Exercise(models.Model):
name = models.CharField(max_length=200)
tags = models.ManyToManyField(Tag)
Here we've created the two models and specified a many-to-many relationship between Exercise and Tag. This means that an Exercise object can have many Tag objects e.g. you can call exercise.tags.all() to get all of the tags for a given Exercise object.
The tricky part comes when we are creating the Workout-Exercise entity. When we use ManyToManyField in Django, it normally automatically creates a mapping model/table that we don't see. However, in the case where we want to store extra information about these relations (as we do in our use-case) we have to use a through model.
On this model we have to define the two foreign keys for the models we are linking along with the data types for the extra field data we want to store. In this case the foreign keys are Workout and Exercise, and the extra data are reps, sets and weight. The model definitions could therefore look like:
class WorkoutExercise(models.Model):
workout = models.ForeignKey(
'Workout',
on_delete=models.CASCADE,
)
exercise = models.ForeignKey(
Exercise,
on_delete=models.CASCADE,
)
reps = models.IntegerField()
sets = models.IntegerField()
weight = models.DecimalField(max_digits=5, decimal_places=2)
class Workout(models.Model):
name = models.CharField(max_length=200)
body_weight = models.DecimalField(max_digits=5, decimal_places=2)
date = models.DateTimeField(auto_now_add=True)
exercises = models.ManyToManyField(
Exercise,
through=WorkoutExercise
)
If you're confused about any of the model data type choices I've recommended, please take a look at Django model docs.
With this set-up you should be able to access and insert all of the data you need. If you need more information on how to access any of the many-to-many relationship data, please look at the Django many-to-many docs.
Sources
https://docs.djangoproject.com/en/3.2/topics/db/models/
https://www.1keydata.com/database-normalization/first-normal-form-1nf.php
https://en.wikipedia.org/wiki/One-to-many_(data_model)
https://docs.djangoproject.com/en/3.2/topics/db/examples/many_to_many
First of all, yes: I've read Django's foreign key and many-to-many documentation, but I'm still not 100% clear on how to implement relationships on a practical level, especially regarding the hierarchy of the relationships.
One-to-one
I am aware of how to form one-to-one relationships. However, on a more conceptual level, which model should contain that reference to the other one? Let's say I have a Citizen, and a Passport. Now, it's obvious that one Citizen can only have a single Passport and viceversa, but, ideally, should the Citizen contain a field referencing to his Passport, or should the Passport model contain a reference to the Citizen it belongs to?
Many-to-many
For the sake of simplicity, let's say I have a Person model and a Trip model (Trip as in going out on a trip somewhere). Many Persons can participate in a single Trip. Or in other words: a Person can participate in many Trips and in any single Trip, a lot of Persons can participate. This looks like a many-to-many relationship, but, again, ideally, which model should contain the definition for the relationship, the Person with a trips field or the Trip with a participants field? And why? Does it even make any practical difference?
Thank you.
This depends on your business logic. As a rule of thumb I'd suggest to think about the admin app. How would you like to add new objects?
When adding new objects, how would you like to add related objects?
Let's say you have these models:
Citizen(models.Model):
name = models.CharField()
Passport(models.Model):
number = models.CharField()
citizen = models.OneToOneField('Citizen', related_name='passport')
When adding new passport object, you have the possibility to add new citizen, if it doesn't yet exist. Since this doesn't look very logical to me, I'd change the relation as:
Citizen(models.Model):
# other fields
passport = models.OneToOneField('Passport', related_name='citizen')
Now we can add a new citizen object in the admin and add the related passport object within the same page.
If you use the admin app, this should lead you to more ergonomical design.
EDIT: expand with many-to-many example
Better example for a m2m relation would be StackOverflow - there are questions and tags. A question has many tags, and a tag has many questions. Let's say the models look like this:
Question(models.Model):
title = models.CharField()
body = models.TextField()
author = models.CharField()
tags = models.ManyToManyField('Tag', related_name='questions')
Tag(models.Model):
name = models.CharField()
Why do we put the relation in Question? This should be very logical - when creating a new question you'd like to set the tags for it. When creating a new tag you don't care about any questions associated with it. You can create a tag and later when creating questions, associate them with the tag.
If a tag doesn't exist yet you can add it from the admin, when adding a new question.
I hope this second example is more palpable.
The theory behind this is called database normalization which is a ladder of best practices you should look up if you want to know more about how to structure your data.
The third form tells us that:
"[Every] non-key [attribute] must provide a fact about the key, the whole key, and nothing but the key."
So in the case of ForeignKey fields it should be on the Child model, because it doesn't tell us anything about the parent, but it does tells us what parent the child belongs to.
The mental model that you should have is Parent and Child. Every relationship has two models. So think of one as the Parent model or the Primary model and think of the other one as the Child model or the Secondary model.
NOTE: Always put your relationship field in the CHILD model.
Here is how I would solve your problems:
For the first one, I will have a mental model that Citizen is the Parent and Passport is the child.
class Citizen(models.Model):
name = models.CharField(max_length=255)
info = models.TextField()
class Passport(models.Model):
owner = models.OneToOneField(Citizen)
unique_no = models.CharField(max_length=30, unique=True)
For the second problem, do the same. I would choose Person as the parent model and Trip as the child model.
class Person(models.Model):
name = models.CharField(max_length=255)
info = models.TextField()
class Trip(models.Model):
person = models.ManyToManyField(Person)
info = models.TextField()
If you have sqlitebrowser, you can use that to open your database and check what tables were created according to your models. Then, you will have a clearer idea as to how Django sees your models.
I am designing a website using django. The idea of a website is to have photographers and each photographer would have an album or more albums and each album would have one or more photo/photos. Currently I have:
class Photo(models.Model):
name=models.ForeignKey('Album')
p=models.ImageField()
def __unicode__(self):
return self.name
class Album(models.Model):
name=models.ForeignKey('Photographer')
def __unicode__(self):
return self.name
class Photographer(models.Model):
name=models.ForeignKey('User')
def __unicode__(self):
return self.name
The question is if you have the same design would you create a directory for each photographer and for each album you would create another directory in the parent directory and then store the photos in that album's directory or store them in the database. I know that is a newbie question but I really need help. If you have other design to make it easier on me please let me know.
Thanks in advance,
Abdul
Use the imagefield to store all the photos at one place and even better store them in cloud storage services. And as to regarding the relation have ForeignKeys in Photo model relating to Album and Photographer. Than use reverse relationship to find out the photos of each album or Photographer.
Your code might look like this`
class Photo(models.Model):
image = ImageField(upload_to="whereever you want to")
album = ForeignKey(Album)
photographer=ForeignKey(Photographer)
and after you get a particular album you can use
Album.set_all and photographer.set_all to get all the photos of a particular album or photographer
`
Yes, store the photos directly in the file system, rather than database. This is a common practise which makes sense for many reasons, mostly related to performance.
If you expect to have thousends of photos then dont keep them one directory, cause that can cause performance issues on popular systems. Instead, design a simple hierarchy appropriate for your application.
There is no need to create directories for photographer or an album. That is what databases are for. You can have one giant folder (specified in upload_to) which will contain all uploaded images for all photographers and albums and then its up for a db to keep track which photos belong to what photographer/album.
By the way you should specify upload_to parameter for ImageField:
models.ImageField(upload_to='images/')
If you really want to create folders for each album, you can pass a function to upload_to parameter and add whatever logic you want there... more about that in the docs here.
I would like to have a model in Django that has multiple pictures associated with it. I'm evaluating possible options.
One picture for one model is easily done with the models.ImageField(...).
However, I would like a array (or set) of pictures. It can be just paths, not necessarily ImageField objects.
The problem is, how do I create that field in a Django model? I am assuming I will need to create a field that is not part of models.WhateverField. Is that possible? Can I define a non-model field, such as:
class MyModel:
name = models.CharField(max_length=10)
picture_list = []
and then do:
def sample_add_picture_view(request):
picture = "sample.jpg"
model = MyModel.objects.get(id=sample_id)
model.picture_list.append(picture)
model.save()
return HttpResponseRedirect('index.html')
Could this be done? If not, what could be a better solution? Thank you !
You need to create two separate models and link them with a ForeignKey field, like so:
class Item(models.Model):
name = models.CharField(max_length=255)
class ItemImage(models.Model):
image = models.ImageField(upload_to="item_images")
item = models.ForeignKey('Item', related_name="images")
It is possible to make a custom field to store multiple items, but it's a really bad idea. You would have to serialise an array into the database, making maintenance very difficult. Using a separate model means you can store extra information such as upload times, image captions etc with little extra effort.
In my Django admin, when I try to view/edit objects from one particular model class the memory usage and CPU rockets up and I have to restart the server. I can view the list of objects fine, but the problem comes when I click on one of the objects. Other models are fine. Working with the object in code (i.e. creating and displaying) is ok, the problem only arises when I try to view an object with the admin interface. The class isn't even particularly exotic:
class Comment(models.Model):
user = models.ForeignKey(User)
thing = models.ForeignKey(Thing)
date = models.DateTimeField(auto_now_add=True)
content = models.TextField(blank=True, null=True)
approved = models.BooleanField(default=True)
class Meta:
ordering = ['-date']
Any ideas? I'm stumped. The only reason I could think of might be that the thing is quite a large object (a few kb), but as I understand it, it wouldn't get loaded until it was needed (correct?).
It's not really a question of how big the Thing object is, but rather of how many you have in your database. That's because for a ForeignKey, by default Django's admin gives you a drop-down list containing all the existing items. If you've got lots and lots, then Django will load them all in order to populate that list. The same is true here of User.
The best way round this is to add the offending field to the raw_id_fields in your ModelAdmin subclass. That will change the representation to a simple textfield for the id, with a pop-up lookup window.