Django Admin保存前处理上传图片:获取文件、校验与查重
Fixing Your Django Image Upload Model
Hey there! Let's get your UploadedImage model working correctly. The main issue you're facing is trying to access request.FILES directly in the model's save method—models don't have access to the request object, so we need to use the self.image field itself to get the uploaded file. Here's how to fix and improve your code:
Key Issues to Address
- Accessing the Uploaded File: Use
self.image.fileinstead ofrequest.FILES['image']—this gives you the actual file object attached to the ImageField. - MD5 Hash Generation: You need to read the file's binary content, but remember to reset the file pointer after reading so PIL can open it later.
- Duplicate Check: Your
hash_existsstatic method was using incorrect syntax—exists()doesn't take keyword arguments; usefilter()first. - Safe ID Generation: Your
generate_namemethod would fail on the first upload (since there are no existing IDs), so we'll add a fallback for that case. - Proper Error Handling: Use Django's
ValidationErrorinstead of a genericValueErrorso the Admin interface displays the error to the user properly.
Updated Model Code
from django.db import models from django.core.exceptions import ValidationError from hashlib import md5 from PIL import Image import os class UploadedImage(models.Model): """Model for uploaded images with metadata and duplicate detection""" name = models.CharField(max_length=255, default='') pub_date = models.DateTimeField(auto_now=True) image = models.ImageField(upload_to='images/') height = models.IntegerField(default=0) width = models.IntegerField(default=0) extension = models.CharField(max_length=20, null=False, default='') hash = models.CharField(max_length=32, null=False, unique=True, db_column='hash', default='') def __str__(self): return self.name @staticmethod def hash_exists(image_hash): # Correct syntax for checking existence return UploadedImage.objects.filter(hash=image_hash).exists() def generate_name(self): # Handle first upload case when no IDs exist yet latest_img = UploadedImage.objects.values('id').order_by('-id').first() if latest_img: img_id = latest_img['id'] + 1 else: img_id = 1 self.name = f"{img_id}_{self.hash[-4:]}" def save(self, *args, **kwargs): # Only process if a new image was uploaded (or updated) if self.image and not self.hash: # Skip if hash already exists (for updates) # Read the file content and reset the pointer file_content = self.image.file.read() self.image.file.seek(0) # Reset so PIL can read it # Generate MD5 hash self.hash = md5(file_content).hexdigest() # Check for duplicates if UploadedImage.hash_exists(self.hash): raise ValidationError("This image has already been uploaded (duplicate detected).") # Get image dimensions and format with Image.open(self.image.file) as img: self.width, self.height = img.size # Get extension from image format or file name self.extension = img.format.lower() if img.format else os.path.splitext(self.image.name)[1].lstrip('.').lower() # Generate a unique name self.generate_name() # Call the parent save method super().save(*args, **kwargs)
Explanation of Key Changes
- File Access:
self.image.filegives us the uploaded file object. We read its content for the hash, then callseek(0)to reset the file pointer—this is crucial because reading the file moves the pointer to the end, and PIL would fail to open an empty file otherwise. - Duplicate Check: Using
filter(hash=image_hash).exists()is the correct way to check if a hash already exists in the database. - Error Handling: Raising
ValidationErrorensures that the Django Admin will display the error message to the user instead of just printing it to the console. - Extension Handling: Fallback to the file name's extension in case PIL can't detect the image format (edge case for rare formats).
- ID Generation: Added a fallback for the first upload when there are no existing records, so
img_iddefaults to 1 instead of throwing an error.
Notes for Usage
- Make sure you have
Pillowinstalled (you mentioned it's imported, so you probably do, but double-check withpip install pillow). - When updating an existing
UploadedImageinstance without changing the image, the hash check and metadata generation will be skipped (sinceself.hashalready exists). - The
hashfield is set tomax_length=32because MD5 hashes are exactly 32 characters long—no need for 50.
内容的提问来源于stack exchange,提问作者user3605780
相关产品推荐
相关产品推荐

