mirror of
https://github.com/daniviga/django-ram.git
synced 2025-08-04 13:17:50 +02:00
Reuse existing file if content is the same
This commit is contained in:
@@ -4,6 +4,7 @@ from django.urls import reverse
|
|||||||
|
|
||||||
from ckeditor_uploader.fields import RichTextUploadingField
|
from ckeditor_uploader.fields import RichTextUploadingField
|
||||||
|
|
||||||
|
from ram.utils import DeduplicatedStorage
|
||||||
from metadata.models import Company, Tag
|
from metadata.models import Company, Tag
|
||||||
from roster.models import RollingStock
|
from roster.models import RollingStock
|
||||||
|
|
||||||
@@ -17,7 +18,9 @@ class Consist(models.Model):
|
|||||||
)
|
)
|
||||||
company = models.ForeignKey(Company, on_delete=models.CASCADE)
|
company = models.ForeignKey(Company, on_delete=models.CASCADE)
|
||||||
era = models.CharField(max_length=32, blank=True)
|
era = models.CharField(max_length=32, blank=True)
|
||||||
image = models.ImageField(upload_to="images/", null=True, blank=True)
|
image = models.ImageField(
|
||||||
|
upload_to="images/", storage=DeduplicatedStorage, null=True, blank=True
|
||||||
|
)
|
||||||
notes = RichTextUploadingField(blank=True)
|
notes = RichTextUploadingField(blank=True)
|
||||||
creation_time = models.DateTimeField(auto_now_add=True)
|
creation_time = models.DateTimeField(auto_now_add=True)
|
||||||
updated_time = models.DateTimeField(auto_now=True)
|
updated_time = models.DateTimeField(auto_now=True)
|
||||||
|
@@ -3,7 +3,7 @@ from django.conf import settings
|
|||||||
from django.dispatch.dispatcher import receiver
|
from django.dispatch.dispatcher import receiver
|
||||||
from django_countries.fields import CountryField
|
from django_countries.fields import CountryField
|
||||||
|
|
||||||
from ram.utils import get_image_preview, slugify
|
from ram.utils import DeduplicatedStorage, get_image_preview, slugify
|
||||||
|
|
||||||
|
|
||||||
class Property(models.Model):
|
class Property(models.Model):
|
||||||
@@ -24,7 +24,9 @@ class Manufacturer(models.Model):
|
|||||||
max_length=64, choices=settings.MANUFACTURER_TYPES
|
max_length=64, choices=settings.MANUFACTURER_TYPES
|
||||||
)
|
)
|
||||||
website = models.URLField(blank=True)
|
website = models.URLField(blank=True)
|
||||||
logo = models.ImageField(upload_to="images/", null=True, blank=True)
|
logo = models.ImageField(
|
||||||
|
upload_to="images/", storage=DeduplicatedStorage, null=True, blank=True
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ["category", "name"]
|
ordering = ["category", "name"]
|
||||||
@@ -43,7 +45,9 @@ class Company(models.Model):
|
|||||||
extended_name = models.CharField(max_length=128, blank=True)
|
extended_name = models.CharField(max_length=128, blank=True)
|
||||||
country = CountryField()
|
country = CountryField()
|
||||||
freelance = models.BooleanField(default=False)
|
freelance = models.BooleanField(default=False)
|
||||||
logo = models.ImageField(upload_to="images/", null=True, blank=True)
|
logo = models.ImageField(
|
||||||
|
upload_to="images/", storage=DeduplicatedStorage, null=True, blank=True
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name_plural = "Companies"
|
verbose_name_plural = "Companies"
|
||||||
@@ -67,7 +71,9 @@ class Decoder(models.Model):
|
|||||||
)
|
)
|
||||||
version = models.CharField(max_length=64, blank=True)
|
version = models.CharField(max_length=64, blank=True)
|
||||||
sound = models.BooleanField(default=False)
|
sound = models.BooleanField(default=False)
|
||||||
image = models.ImageField(upload_to="images/", null=True, blank=True)
|
image = models.ImageField(
|
||||||
|
upload_to="images/", storage=DeduplicatedStorage, null=True, blank=True
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "{0} - {1}".format(self.manufacturer, self.name)
|
return "{0} - {1}".format(self.manufacturer, self.name)
|
||||||
|
@@ -1,8 +1,29 @@
|
|||||||
import os
|
import os
|
||||||
|
import hashlib
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from django.utils.html import format_html
|
from django.utils.html import format_html
|
||||||
from django.utils.text import slugify as django_slugify
|
from django.utils.text import slugify as django_slugify
|
||||||
|
from django.core.files.storage import FileSystemStorage
|
||||||
|
|
||||||
|
|
||||||
|
class DeduplicatedStorage(FileSystemStorage):
|
||||||
|
"""
|
||||||
|
A derived FileSystemStorage class that compares already existing files
|
||||||
|
(with the same name) with new uploaded ones and stores new file only if
|
||||||
|
sha256 hash on is content is different
|
||||||
|
"""
|
||||||
|
|
||||||
|
def save(self, name, content, max_length=None):
|
||||||
|
if super().exists(name):
|
||||||
|
new = hashlib.sha256(content.file.getbuffer()).hexdigest()
|
||||||
|
with open(super().path(name), "rb") as file:
|
||||||
|
file_binary = file.read()
|
||||||
|
old = hashlib.sha256(file_binary).hexdigest()
|
||||||
|
if old == new:
|
||||||
|
return name
|
||||||
|
|
||||||
|
return super().save(name, content, max_length)
|
||||||
|
|
||||||
|
|
||||||
def git_suffix(fname):
|
def git_suffix(fname):
|
||||||
|
@@ -9,7 +9,7 @@ from django.utils.safestring import mark_safe
|
|||||||
|
|
||||||
from ckeditor_uploader.fields import RichTextUploadingField
|
from ckeditor_uploader.fields import RichTextUploadingField
|
||||||
|
|
||||||
from ram.utils import get_image_preview
|
from ram.utils import DeduplicatedStorage, get_image_preview
|
||||||
from metadata.models import (
|
from metadata.models import (
|
||||||
Property,
|
Property,
|
||||||
Scale,
|
Scale,
|
||||||
@@ -20,11 +20,6 @@ from metadata.models import (
|
|||||||
RollingStockType,
|
RollingStockType,
|
||||||
)
|
)
|
||||||
|
|
||||||
# class OverwriteMixin(FileSystemStorage):
|
|
||||||
# def get_available_name(self, name, max_length):
|
|
||||||
# self.delete(name)
|
|
||||||
# return name
|
|
||||||
|
|
||||||
|
|
||||||
class RollingClass(models.Model):
|
class RollingClass(models.Model):
|
||||||
identifier = models.CharField(max_length=128, unique=False)
|
identifier = models.CharField(max_length=128, unique=False)
|
||||||
@@ -137,7 +132,12 @@ class RollingStockDocument(models.Model):
|
|||||||
RollingStock, on_delete=models.CASCADE, related_name="document"
|
RollingStock, on_delete=models.CASCADE, related_name="document"
|
||||||
)
|
)
|
||||||
description = models.CharField(max_length=128, blank=True)
|
description = models.CharField(max_length=128, blank=True)
|
||||||
file = models.FileField(upload_to="files/", null=True, blank=True)
|
file = models.FileField(
|
||||||
|
upload_to="files/",
|
||||||
|
storage=DeduplicatedStorage(),
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
)
|
||||||
|
|
||||||
class Meta(object):
|
class Meta(object):
|
||||||
unique_together = ("rolling_stock", "file")
|
unique_together = ("rolling_stock", "file")
|
||||||
@@ -158,7 +158,9 @@ class RollingStockImage(models.Model):
|
|||||||
rolling_stock = models.ForeignKey(
|
rolling_stock = models.ForeignKey(
|
||||||
RollingStock, on_delete=models.CASCADE, related_name="image"
|
RollingStock, on_delete=models.CASCADE, related_name="image"
|
||||||
)
|
)
|
||||||
image = models.ImageField(upload_to="images/", null=True, blank=True)
|
image = models.ImageField(
|
||||||
|
upload_to="images/", storage=DeduplicatedStorage, null=True, blank=True
|
||||||
|
)
|
||||||
is_thumbnail = models.BooleanField()
|
is_thumbnail = models.BooleanField()
|
||||||
|
|
||||||
def image_thumbnail(self):
|
def image_thumbnail(self):
|
||||||
|
Reference in New Issue
Block a user