I'm trying to model my app and I've come across this problem:
I have a Slides which can be images, text or videos.
Also, images and videos can be in many different slides and presentations of the same user.
I also want to store which presentation the slide belongs to and the insert date (slides must have an order).
class Slide(models.Model):
user = models.ForeignKey(User)
presentation = models.ForeignKey(Presentation)
insert_date = models.DateTimeField(auto_now_add=True)
-
class Image(models.Model):
def get_image_path(instance, filename):
return os.path.join('users/images', str(instance.id), filename)
user = models.ForeignKey(User)
slide = models.ManyToManyField(Slide)
image = models.ImageField(upload_to=get_image_path)
insert_date = models.DateTimeField(, auto_now_add=True)
-
class SlideText(models.Model):
slide = models.OneToOneField(Slide)
text = models.TextField()
[Video model will be similar to Image]
Problems with this model are that a Slide can be empty, and that a slide can contain both Images and Texts (as well as Video) and it shouldn't.
I can image that this would be a common problem: certain Item have some information related depeding on the "type of Item" (that'd be Slide of image, Slide of text...), so what's the common solution ?
any help really appreciated! thanks!
Generic relations are what you want.
Related
What is the best practice to choose one item from a m2m relationship?
Lets say I've got an album of photos:
class Photo(models.Model):
img = models.FileField()
class Album(models.Model):
photos = models.ManyToManyField("Photo")
But now I also want to pick one photo as a cover. I could use a Foreign Key in Album to one Photo, but then I'd always need to check whether this photo is actually in the photos of that album. Is there a better way?
Sorry for the basic question I just somehow can't find the right words to google it.
Thanks,
Michael
You can make a custom through=… model [Django-doc] with a boolean cover that is set to True in case it is the cover item:
from django.db.models import Q, UniqueConstraint
class Photo(models.Model):
img = models.FileField()
class Album(models.Model):
photos = models.ManyToManyField(Photo, through='AlbumPhoto')
class AlbumPhoto(models.Model):
photo = models.ForeignKey(Photo, on_delete=models.CASCADE)
album = models.ForeignKey(Album, on_delete=models.CASCADE)
cover = models.BooleanField(default=False)
class Meta:
constraints = [
UniqueConstraint(fields=['photo', 'album'], name='no_photo_multiple_times'),
UniqueConstraint(fields=['album'], condition=Q(cover=True), name='one_cover_per_album'),
]
The first constraint guarantees that you can not add the same Photo multiple times to the same Album, whereas the second condition gurantees that each Album has at most one Photo for which cover=True.
Imagine that you have a product which has a cover URL (cover is presented to customers on the landing page) and a list of image URLs (these images show different sides of the product)
problem: how to separate cover from other images?
class Image(models.Model):
url = models.URLField()
class Product(models.Model):
cover = ...?
album_images = ...?
Using ForeignKey in the Image model is not an option because when using product.image_set.all() you will get all images including the cover.
Any suggestions will be appreciated.
One common way would be:
class Product(models.Model):
...
class Image(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name="images")
URL = models.URLField()
is_cover = models.BooleanField()
Because of the "related_name" you can access the images from the product instance.
prod = Product.objects.get(pk=123)
cover = [x for x in prod.images if x.is_cover]
To find the cover image best to add a method to the Product so you can call something like prod.get_cover() or so.
I have the folllowing class model in my Django website:
class Buy(models.Model):
category = models.ForeignKey(Category, related_name='sell', on_delete=models.CASCADE)
title = models.CharField(max_length=100)
image = models.FileField()
image2 = models.FileField(blank=True)
description = models.CharField(max_length=300)
date = models.DateField(default=timezone.now)
buy_price = models.DecimalField(max_digits=6, decimal_places=2)
sell_price = models.DecimalField(max_digits=6, decimal_places=2)
seller = models.ForeignKey(Seller, on_delete=models.PROTECT)
showcase = models.BooleanField(default=False)
As you can see, I store photos files with 2 fields: image and image2. But now my client requested me to add more photos. My doubt is:
Should I continue adding new fields to this class, for example, image3, image4, image5 and so on? The problem I see: not every records will have so many photos and the most of them will become "empty".
Should I only upload the new photos without saving their names into the database? In this way, the new photos should follow some name related to the image class field. I mean, unique_photo_1.jpg goes inside the image field, unique_photo_2.jpg is not saved into the database but is related to this field, as well as the unique_photo_3.jpg.
What is the best practice?
Thank you!
On #1, the best practice is to follow database normalization principles, and create a separate Image model that relates back to your Buy model. If you anticipate that the images may be reused in several Buy model instances, use many-to-many relationships; otherwise, use many-to-one (i.e. ForeignKey). That means removing image and image2 from your Buy model, and then creating e.g.:
class Image(models.Model):
image = models.FileField()
image_type = models.CharField()
buy = models.ForeignKey(Buy, on_delete=models.PROTECT)
By #2, if you mean you're considering skipping using FileField or ImageField to instead write code that will search for files in some storage space, then that doesn't sound like a good idea, because then you're divorcing your file (meta)data from the rest of your database contents. Using FiledField/ImageField will also make it much easier to use storage backends such as AWS S3.
I have a model called Listing. Users can upload up to 20 photos for each Listing.
My question is: Performance-wise, is it better to create 20 fields for Listing, and leave them blank if the user doesn't fill them, or create a ListingPhoto foreignkey and create 1 ListingPhoto for each uploaded image?
class Listing(models.Model):
img_1 = models.ImageField(blank=True)
img_2 = models.ImageField(blank=True)
...
img_20 = models.ImageField(blank=True)
OR
class Listing(models.Model):
...
class ListingPhoto(models.Model):
listing = models.ForeignKey(Listing, on_delete=models.CASCADE)
photo = = models.ImageField()
Well, using 20 image field will be faster for sure as there is no joins required when you are retrieving these images. But this is not very flexible. You can consider using ArrayField or JSONField to be both flexible and require no joins.
I have a Django app where users leave comments for each other. I'm now going to add a photo feature. I need some help in formulating the data model(s) for this photo feature.
Under this feature, users will be able to upload photos in a common area of the website, where the said photos will appear most-recent first. This is simple enough, so let's now add two complications to the feature:
1) For each photo entry, uploaders will have the option of including
additional photos (as a stream). The first photo will then be treated
as a cover photo that is shown in the common area of the website. Upon
clicking this cover, the entire stream will become visible.
2) For each photo entry, any viewer will be able to reply with a
photo of their own. If that happens, the two photos will be put
together as a stream, with the reply treated as a cover photo, to
appear in the common area of the website. Clicking this cover photo
will show both entries one after the other, thus showing the users
what the original photo was, and what the reply to it was.
How should I model the above requirements? I can't seem to wrap my head around this. Something like:
class PhotoReply(models.Model):
owner = models.ForeignKey(User)
which_photo = models.ForeignKey(Photo)
class Photo(models.Model):
owner = models.ForeignKey(User)
is_stream = models.BooleanField(default=False)
image = models.ForeignKey(upload_to=upload_photo_to_location, storage=OverwriteStorage())
upload_time = models.DateTimeField(db_index=True, auto_now_add=True)
stream_id = models.IntegerField(default=0)
is_reply = models.BooleanField(default=False)
reply = models.ForeignKey(PhotoReply, blank=True, null=True)
But there are obvious weakness, inconsistencies in that. Please advise.
Did I understand you correctly that every photo can become a "cover photo", as soon as a user replies to it with another photo? If that's the case, then you only need to keep track of the "children" that a photo has, to know if you want to display it as "cover" or not.
class Photo(models.Model):
owner = models.ForeignKey(User)
parent = models.ForeignKey('self', db_index=True, null=True, default=None)
child_count = models.IntegerField(default=0, db_index=True)
image = models.ForeignKey(upload_to=loc)
upload_time = models.DateTimeField(db_index=True)
So a photo that has > 0 children is a "cover". And for each "cover" photo "x" you just need to look to look up all photos that have "x" as a parent.