160 lines
4.5 KiB
Python
160 lines
4.5 KiB
Python
import os
|
|
from django.db import models
|
|
from django.utils.text import slugify
|
|
from mptt.models import MPTTModel, TreeForeignKey
|
|
|
|
|
|
def thing_picture_upload_path(instance, filename):
|
|
"""Generate a custom path for thing pictures in format: <id>-<name>.<extension>"""
|
|
extension = os.path.splitext(filename)[1]
|
|
safe_name = slugify(instance.name)
|
|
if instance.pk:
|
|
return f'things/{instance.pk}-{safe_name}{extension}'
|
|
else:
|
|
return f'things/temp-{safe_name}{extension}'
|
|
|
|
|
|
class BoxType(models.Model):
|
|
"""A type of storage box with specific dimensions."""
|
|
|
|
name = models.CharField(max_length=255)
|
|
width = models.PositiveIntegerField(help_text='Width in millimeters')
|
|
height = models.PositiveIntegerField(help_text='Height in millimeters')
|
|
length = models.PositiveIntegerField(help_text='Length in millimeters')
|
|
|
|
class Meta:
|
|
ordering = ['name']
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
|
|
class Box(models.Model):
|
|
"""A storage box in the lab."""
|
|
|
|
id = models.CharField(
|
|
max_length=10,
|
|
primary_key=True,
|
|
help_text='Alphanumeric identifier (max 10 characters)'
|
|
)
|
|
box_type = models.ForeignKey(
|
|
BoxType,
|
|
on_delete=models.PROTECT,
|
|
related_name='boxes'
|
|
)
|
|
|
|
class Meta:
|
|
verbose_name_plural = 'boxes'
|
|
|
|
def __str__(self):
|
|
return self.id
|
|
|
|
|
|
class ThingType(MPTTModel):
|
|
"""A hierarchical type/category for things stored in boxes."""
|
|
|
|
name = models.CharField(max_length=255)
|
|
parent = TreeForeignKey(
|
|
'self',
|
|
on_delete=models.CASCADE,
|
|
null=True,
|
|
blank=True,
|
|
related_name='children'
|
|
)
|
|
|
|
class MPTTMeta:
|
|
order_insertion_by = ['name']
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
|
|
class Thing(models.Model):
|
|
"""An item stored in a box."""
|
|
|
|
name = models.CharField(max_length=255)
|
|
thing_type = models.ForeignKey(
|
|
ThingType,
|
|
on_delete=models.PROTECT,
|
|
related_name='things'
|
|
)
|
|
box = models.ForeignKey(
|
|
Box,
|
|
on_delete=models.PROTECT,
|
|
related_name='things'
|
|
)
|
|
description = models.TextField(blank=True)
|
|
picture = models.ImageField(upload_to=thing_picture_upload_path, blank=True)
|
|
|
|
class Meta:
|
|
ordering = ['name']
|
|
|
|
def save(self, *args, **kwargs):
|
|
"""Override save to rename picture file after instance gets a pk."""
|
|
if self.picture and not self.pk:
|
|
picture = self.picture
|
|
super().save(*args, **kwargs)
|
|
new_path = thing_picture_upload_path(self, picture.name)
|
|
if picture.name != new_path:
|
|
try:
|
|
old_path = self.picture.path
|
|
if os.path.exists(old_path):
|
|
new_full_path = os.path.join(os.path.dirname(old_path), os.path.basename(new_path))
|
|
os.rename(old_path, new_full_path)
|
|
self.picture.name = new_path
|
|
super().save(update_fields=['picture'])
|
|
except (AttributeError, FileNotFoundError):
|
|
pass
|
|
else:
|
|
super().save(*args, **kwargs)
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
|
|
def thing_file_upload_path(instance, filename):
|
|
"""Generate a custom path for thing files in format: things/files/<thing_id>/<filename>"""
|
|
return f'things/files/{instance.thing.id}/{filename}'
|
|
|
|
|
|
class ThingFile(models.Model):
|
|
"""A file attachment for a Thing."""
|
|
|
|
thing = models.ForeignKey(
|
|
Thing,
|
|
on_delete=models.CASCADE,
|
|
related_name='files'
|
|
)
|
|
file = models.FileField(upload_to=thing_file_upload_path)
|
|
title = models.CharField(max_length=255, help_text='Descriptive name for the file')
|
|
uploaded_at = models.DateTimeField(auto_now_add=True)
|
|
|
|
class Meta:
|
|
ordering = ['-uploaded_at']
|
|
|
|
def __str__(self):
|
|
return f'{self.thing.name} - {self.title}'
|
|
|
|
def filename(self):
|
|
"""Return the original filename."""
|
|
return os.path.basename(self.file.name)
|
|
|
|
|
|
class ThingLink(models.Model):
|
|
"""A hyperlink for a Thing."""
|
|
|
|
thing = models.ForeignKey(
|
|
Thing,
|
|
on_delete=models.CASCADE,
|
|
related_name='links'
|
|
)
|
|
url = models.URLField(max_length=2048)
|
|
title = models.CharField(max_length=255, help_text='Descriptive title for the link')
|
|
uploaded_at = models.DateTimeField(auto_now_add=True)
|
|
|
|
class Meta:
|
|
ordering = ['-uploaded_at']
|
|
|
|
def __str__(self):
|
|
return f'{self.thing.name} - {self.title}'
|