Django Model store image file size - django

I am using Django 1.6. I have a model for uploading image files that looks like this.
class Image(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
url = models.ImageField(upload_to=get_image_path,
null=True,
blank=True,
height_field = 'height',
width_field = 'width',
verbose_name='Image')
height = models.IntegerField(blank=True)
width = models.IntegerField(blank=True)
size = models.IntegerField(blank=True)
format = models.CharField(max_length=50)
caption = models.CharField(max_length=255)
def clean(self):
self.size = self.url.size
class Meta:
db_table = 'image'
As you can see, I am storing the size of the image when the clean() method is called. This works for what I want to do, but is this best practise? Is there a better way to automatically save the image's file size when saving?
The second part of my question is how can I get the content type as well?
Thanks,
Mark

Model.clean() should be used for validation - do not use it to update/save the data, but rather use it to correct any invalid data (or throw an exception/error message).
You may want to consider not even storing the size of the image in the database, given that you can access it from the ImageField - it eliminates the possibility of the data becoming inconsistent as it changes over time.
I believe this question/answer should address your second question.

For the first question
Check out the Python Imaging Library PIL on this thread.
from PIL import Image
im=Image.open(filepath)
im.size # (width,height) tuple
For the second question
HttpRequest.META, more specifically HttpRequest.META.get('CONTENT_TYPE')
from this thread

Related

Django one to many models - How to create the "many" when the "one" is created?

There are a lot of questions along these lines, but so far as I can tell, but many are quite old and I haven't found one that helped me understand my use case. I think I want to be looking at signals, but I'm not clear on exactly what I should be doing in terms of Django patterns and best practices.
Here's my Model code:
from django.db import models
from django.db.models.fields import SlugField, TextField
from django.utils import timezone
# Hexes have Terrains, which impart intrinsic properties, like base speed and .png image
# They're NOT 1:1 with Hexes IE) You might want Slow and Normal Grasslands with the same image
class Terrain(models.Model):
FAST = 'F'
NORMAL = 'N'
SLOW = 'S'
SPEED_CHOICES = [
(FAST, 'Fast'),
(NORMAL, 'Normal'),
(SLOW, 'Slow'),
]
name = models.CharField(max_length=64)
speed = models.CharField(max_length=6, choices=SPEED_CHOICES, default=NORMAL)
image = models.ImageField(upload_to='images/', blank=True)
def __str__(self):
return self.name
# Grids establish the dimensions of a HexMap. Grids may be used in many HexMaps
class Grid(models.Model):
name = models.CharField(max_length=64)
size = models.IntegerField(default=72)
num_rows = models.IntegerField(default=10)
num_cols= models.IntegerField(default=10)
def __str__(self):
return self.name
# Hexes represent the "tiles" on a Grid. A single Hex may appear in many Grids
class Hex(models.Model):
name = models.CharField(max_length=64, blank=True, null=True)
terrain = models.ForeignKey(Terrain, on_delete=models.CASCADE)
grids = models.ManyToManyField(Grid)
def __str__(self):
return self.id
class Meta:
verbose_name_plural = "Hexes"
# Locations are coordinal points on a HexMap. They may contain many Annotations
class Location(models.Model):
# HexMap Name + row + col, for a consistent way to ID locations
name = models.CharField(max_length=64, blank=True, null=True)
friendly_name = models.CharField(max_length=64, blank=True, null=False)
# TODO constrain these to the sizes specified in the grids? Sane defaults?
row = models.PositiveIntegerField()
col = models.PositiveIntegerField()
# Authors create Annotations
# TODO create Players and make Authors extend it so we can do rights and perms
class Author(models.Model):
first_name = models.CharField(max_length=64, blank=False, null=False)
last_name = models.CharField(max_length=64, blank=False, null=False)
join_date = timezone.now()
def __str__(self):
return self.first_name + " " + self.last_name
# Annotations are entries created by Authors. A location may have many Annotations
class Annotation(models.Model):
name = models.CharField(max_length=64, blank=False, null=False)
pubdate = join_date = timezone.now()
content = TextField()
# Hexmaps put it all together and allow fetching of Locations by (r,c) coords
class HexMap(models.Model):
name = models.CharField(max_length=64, blank=True, null=True)
rows = {}
cols = {}
#TODO - how do I create grid.num_rows * grid.num_cols locations and put them into the dicts?
I am probably making a ton of mistakes as I've not worked in Django in close to a decade. Any constructive feedback is appreciated in the comments. Most of the relationships are not coded in, as I'm not sure how they should look.
My specific question, however, is related the HexMap and Location objects.
When a HexMap is created, I want to create a set of empty Locations with a default name value matching a naming convention like HexMap.name + row + col and fill the row{} and col{} dicts of the new HexMap with them.
My instinct is that a Hexmap needs a Grid in order to be created, so I could create a nested loop inside HexMap with grid.num_rows as as the outer loop and num_cols as the inner loop. Each iteration through the inner loop builds a row of Locations as a dict. Each iteration of the outer loop adds that row to the rows{} dict. One thing I'm not certain about is how I'd get the grid.num_rows and grid.num_cols when the HexMap isn't even created yet, and doesn't have a grid associated.
Is this anywhere near the right way to do this in Django, or am I off the mark? Many of the answers and online tutorials seem to indicate that I need to look at signals, but I'm just not really clear on whether or not that's the case. Also, if so, I could use some similar examples to review. The Django docs are great and helpful, but when you're "new" to it all, it can be hard to parse and adopt the examples.
Found this question, which is the question I really should have asked in the first place. While a few years old, I think it's still the right answer and helped direct me to the appropriate documentation. Still not sure what my next step is, thinking I need some sane defaults and an override of the save method to use those if the instances of the other fields don't exist yet. I'm still working out wether or not I have a valid use case for signals here. I think that I will eventually when I add more apps to the project and actions in those apps need to interact with the models in this app.

Saving images names into the database - Django

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.

How do I only accept uploaded images with a 1:1 (square) aspect ratio and throw an error otherwise?

The admin is able to upload an image but I would like to restrict it to only being a square image.
Here is my models.py file:
class Author(models.Model):
author_name = models.CharField(max_length=200)
author_image = models.ImageField(upload_to=upload_location,
null=True,
blank=True,)
author_bio = models.TextField()
def __str__(self):
return self.author_name
ImageField has height_field and width_field properties, but according to the docs, those only get auto-populated after the image is saved.
In order to do it before the image is uploaded, I think you're going to need jQuery. Check out this question/answer, as it seems like what you want to do. You would alter that code to compare that width == height to check for perfect squareness. AJAX would give you the option of the user uploading the image without the page needing to reload.

Django bulk edit form

In my project I have a many-to-one relation with one Serie having up to 20 samples. I'm trying to implement a system where a user uploads a (csv)file and is then sent to a page where they can fill in any missing data. I haven't found a way to implement this and was looking for some help. The idea is that I want a table with each row having 4 fields, which are linked to the sample in question and the table will have as many rows as there were samples in the (csv)file. Furthermore, if data is already present for one of the fields, I want it to prefill the field in question. In the end, the updated data is committed by pressing a single save button at the bottom so it's supposed to be one big form.
I've added the models below. The attributes which I want to update in the form are index, pool and remarks, which may be present in the csv file already but often they aren't.
Can anyone give me any tips on how to implement this or hint at methods which are best for implementing something like this?
Models
class Sample(models.Model):
name = models.CharField(max_length=20)
pool = models.ForeignKey(Pool, blank=True, null=True)
serie = models.ForeignKey(Serie, blank=True, null = True)
index = models.ForeignKey(Index, blank=True, null=True)
remarks = models.CharField(max_length=50, blank=True)
def __str__(self):
return self.name
class Serie(models.Model):
a_type = models.ForeignKey(Atype)
name = models.IntegerField()
def __str__(self):
return str(self.a_type)+'-'+str(self.name)
Views
def serie_edit(request,serie_id):
try:
serie = Serie.objects.get(pk=serie_id)
except Serie.DoesNotExist:
raise Http404
index_list = Index.objects.all()
sample_list = Sample.objects.filter(serie__id = serie_id)
return render(request, 'samples/serie_edit.html', {'serie':serie, 'sample_list':sample_list, 'index_list':index_list})

Django imagekit and dynamic paths

I'm using imagekit provided at: imagekit
So, I've defined two class model:
class Photo(models.Model):
#photo_wrapper = models.ForeignKey(PhotoWrapper, blank=True, null=True)
original_image = models.ImageField(upload_to='static/photos')
thumbnail = ImageSpec([Adjust(contrast=1.2, sharpness=1.1),
resize.Crop(50, 50)], image_field='original_image',
format='JPEG', quality=90)
num_views = models.PositiveIntegerField(editable=False, default=0)
class IKOptions:
# This inner class is where we define the ImageKit options for the model
spec_module = 'myspecs.specs'
cache_dir = 'static/photos'
image_field = 'original_image'
save_count_as = 'num_views'
class Country(models.Model):
country_name = models.CharField(max_length=250)
country_photo = models.ForeignKey(Photo, blank=True, null=True)
def __unicode__(self):
return '%s' % self.country_name
The problem is that each photo is created in the "static/photos" path.
My intent is to save the image and the thumbnail with a dynamic path, based on the country name..
For example, for the country "Argentina", the dynamic path will be "static/photos/Argentina/"
How can I accomplish that?
It looks like you're mixing two different versions of ImageKit. The newer versions (1.0+) no longer use the inner IKOptions class, so all of that is being ignored. (The save_count_as functionality was also removed.)
If you want to control the cache filename, the ImageSpec constructor accepts a cache_to kwarg which—like ImageField's upload_to—can be a callable. Here's the current documentation for cache_to:
Specifies the filename to use when saving the image
cache file. This is modeled after ImageField's ``upload_to`` and
can be either a string (that specifies a directory) or a
callable (that returns a filepath). Callable values should
accept the following arguments:
- instance -- The model instance this spec belongs to
- path -- The path of the original image
- specname -- the property name that the spec is bound to on
the model instance
- extension -- A recommended extension. If the format of the
spec is set explicitly, this suggestion will be
based on that format. if not, the extension of the
original file will be passed. You do not have to use
this extension, it's only a recommendation.
So you'll just have to create a function that accepts those arguments and returns the path you want, and use that in your model like so:
class Photo(models.Model):
thumbnail = ImageSpec(..., cache_to=my_cache_to_function)