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.
Related
I have a model that has an ImageField. I want users to be able to upload multiple images for an object of the model - not only a single image. How can this be done? Whether with and image field or another approach.
You cannot store several images in one ImageField.
One solution for this problem would be to create an additional model (I called it "Attachment" for my social network pet project, call your's whatever should suit you) and have it reference the original model in a Foreign key. That way you can upload as many images as you want and create an instance of that new model for each new image.
Example Attachment model:
class Attachment(DatetimeCreatedMixin, AuthorMixin):
class AttachmentType(models.TextChoices):
PHOTO = "Photo", _("Photo")
VIDEO = "Video", _("Video")
file = models.ImageField('Attachment', upload_to='attachments/')
file_type = models.CharField('File type', choices=AttachmentType.choices, max_length=10)
publication = models.ForeignKey(TheOriginalModelYouUsedImageFieldIn, on_delete=models.CASCADE, verbose_name='Model that uses the image field')
class Meta:
verbose_name = 'Attachment'
verbose_name_plural = 'Attachments'
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 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.
In my primary class model Deals, I have certain fields as description, price, date_created etc. I now have to add some fields having sub-fields to it. For eg, I'm trying to add an age field to Deals. This age field further has subfields (like score_for_kid, score_for_baby, score_for_old etc), and I want to edit these scores from the admin.
Here is my models.py:
class Deals(models.Model):
description = models.TextField()
price = models.DecimalField(max_digits=7, decimal_places=2)
url = models.URLField(verify_exists=False)
currency = models.CharField(max_length=3)
created_date = models.DateField(auto_now_add=True)
kid_score = models.IntegerField(max_length=2,default=0)
teenager_score = models.IntegerField(max_length=2,default=0)
youth_score = models.IntegerField(max_length=2,default=0)
old_score = models.IntegerField(max_length=2,default=0)
I don't want to store all these sub fields (around 20-25 in 4 different fields) in the model, instead an age field connected to these subfields. Would a ManyToManyField work for this?
The underlying requirement is that when a user selects a subfield (say kids) on the browser, all the objects having higher kid scores are displayed.
I'm very new to Django and any help on this would be great. Thanks.
If I understand your question properly ou need to use ForeignKey fields.
class Deals(models.Model):
description = models.TextField()
price = models.DecimalField(max_digits=7, decimal_places=2)
#...
age = models.ForeignKey(Age)
class Age(models.Model):
kid_score = models.IntegerField(max_length=2,default=0)
teenager_score = models.IntegerField(max_length=2,default=0)
#...
Have a good read of the docs on Models. You might also find it useful to do some reading on relational databases / basic sql.
When you come to edit your objects in the django admin, you'll probably want to use an InlineModelAdmin class.
UPDATE
re-reading your question, it sounds like you might simply want to show / hide these additional fields on the main Deal model. If this is the case then you want to use fieldsets in the admin, with a class 'collapse'. There's an example in the docs.
If you want each Deal record to have multiple kid_score's associated with it then you want a foreign key. If each Deal can only have one kid_score then you need to keep the kid_score (and other) fields in the main model (if this is confusing then definitely do some reading on sql / relational databases).