[IMP] HORILLA: Updated FileField in models to use dynamic upload path via upload_path function

This commit is contained in:
Horilla
2025-07-30 13:02:47 +05:30
parent f99eeafddd
commit da06922424
11 changed files with 67 additions and 46 deletions

View File

@@ -12,7 +12,7 @@ from django.utils.translation import gettext_lazy as _
from base.horilla_company_manager import HorillaCompanyManager
from base.models import Company
from employee.models import Employee
from horilla.models import HorillaModel
from horilla.models import HorillaModel, upload_path
class AssetCategory(HorillaModel):
@@ -188,9 +188,7 @@ class AssetDocuments(HorillaModel):
asset_report = models.ForeignKey(
"AssetReport", related_name="documents", on_delete=models.CASCADE
)
file = models.FileField(
upload_to="asset/asset_report/documents/", blank=True, null=True
)
file = models.FileField(upload_to=upload_path, blank=True, null=True)
objects = models.Manager()
class Meta:
@@ -209,7 +207,7 @@ class ReturnImages(HorillaModel):
- image: A FileField for uploading the image file (optional).
"""
image = models.FileField(upload_to="asset/return_images/", blank=True, null=True)
image = models.FileField(upload_to=upload_path, blank=True, null=True)
class AssetAssignment(HorillaModel):

View File

@@ -32,7 +32,7 @@ from base.methods import is_company_leave, is_holiday
from base.models import Company, EmployeeShift, EmployeeShiftDay, WorkType
from employee.models import Employee
from horilla.methods import get_horilla_model_class
from horilla.models import HorillaModel
from horilla.models import HorillaModel, upload_path
from horilla_audit.models import HorillaAuditInfo, HorillaAuditLog
# to skip the migration issue with the old migrations
@@ -575,7 +575,7 @@ class Attendance(HorillaModel):
class AttendanceRequestFile(HorillaModel):
file = models.FileField(upload_to="attendance/request_files")
file = models.FileField(upload_to=upload_path)
class AttendanceRequestComment(HorillaModel):

View File

@@ -19,7 +19,7 @@ from django.utils.translation import gettext_lazy as _
from base.horilla_company_manager import HorillaCompanyManager
from horilla import horilla_middlewares
from horilla.horilla_middlewares import _thread_locals
from horilla.models import HorillaModel
from horilla.models import HorillaModel, upload_path
from horilla_audit.models import HorillaAuditInfo, HorillaAuditLog
# Create your models here.
@@ -78,7 +78,7 @@ class Company(HorillaModel):
city = models.CharField(max_length=50)
zip = models.CharField(max_length=20)
icon = models.FileField(
upload_to="base/icon",
upload_to=upload_path,
null=True,
)
objects = models.Manager()
@@ -826,7 +826,7 @@ class RotatingShiftAssign(HorillaModel):
class BaserequestFile(models.Model):
file = models.FileField(upload_to="base/request_files")
file = models.FileField(upload_to=upload_path)
objects = models.Manager()
@@ -1487,7 +1487,7 @@ class Attachment(models.Model):
Attachment model for multiple attachments in announcements.
"""
file = models.FileField(upload_to="attachments/")
file = models.FileField(upload_to=upload_path)
def __str__(self):
return self.file.name

View File

@@ -34,7 +34,7 @@ from base.models import (
from employee.methods.duration_methods import format_time, strtime_seconds
from horilla import horilla_middlewares
from horilla.methods import get_horilla_model_class
from horilla.models import HorillaModel, has_xss
from horilla.models import HorillaModel, has_xss, upload_path
from horilla_audit.methods import get_diff
from horilla_audit.models import HorillaAuditInfo, HorillaAuditLog
@@ -78,9 +78,7 @@ class Employee(models.Model):
employee_last_name = models.CharField(
max_length=200, null=True, blank=True, verbose_name=_("Last Name")
)
employee_profile = models.ImageField(
upload_to="employee/profile", null=True, blank=True
)
employee_profile = models.ImageField(upload_to=upload_path, null=True, blank=True)
email = models.EmailField(max_length=254, unique=True)
phone = models.CharField(
max_length=25,
@@ -775,7 +773,7 @@ class EmployeeBankDetails(HorillaModel):
class NoteFiles(HorillaModel):
files = models.FileField(upload_to="employee/NoteFiles", blank=True, null=True)
files = models.FileField(upload_to=upload_path, blank=True, null=True)
objects = models.Manager()
def __str__(self):
@@ -810,7 +808,7 @@ class PolicyMultipleFile(HorillaModel):
PoliciesMultipleFile model
"""
attachment = models.FileField(upload_to="employee/policies")
attachment = models.FileField(upload_to=upload_path)
class Policy(HorillaModel):
@@ -942,9 +940,7 @@ class DisciplinaryAction(HorillaModel):
validators=[validate_time_format],
)
start_date = models.DateField(null=True)
attachment = models.FileField(
upload_to="employee/discipline", null=True, blank=True
)
attachment = models.FileField(upload_to=upload_path, null=True, blank=True)
objects = HorillaCompanyManager("employee_id__employee_work_info__company_id")
def __str__(self) -> str:

View File

@@ -11,7 +11,7 @@ from base.horilla_company_manager import HorillaCompanyManager
from base.models import Company, Department, JobPosition, Tags
from employee.models import Employee
from horilla import horilla_middlewares
from horilla.models import HorillaModel
from horilla.models import HorillaModel, upload_path
from horilla_audit.methods import get_diff
from horilla_audit.models import HorillaAuditInfo, HorillaAuditLog
@@ -211,7 +211,7 @@ class Comment(HorillaModel):
class Attachment(HorillaModel):
file = models.FileField(upload_to="Tickets/Attachment")
file = models.FileField(upload_to=upload_path)
description = models.CharField(max_length=100, blank=True, null=True)
format = models.CharField(max_length=50, blank=True, null=True)
ticket = models.ForeignKey(

View File

@@ -9,6 +9,7 @@ information, audit logging, and active/inactive status management.
"""
import re
from uuid import uuid4
from auditlog.models import AuditlogHistoryField
from auditlog.registry import auditlog
@@ -17,6 +18,7 @@ from django.core.exceptions import ValidationError
from django.db import models
from django.db.models.fields.files import FieldFile
from django.urls import reverse
from django.utils.text import slugify
from django.utils.translation import gettext as _
from horilla.horilla_middlewares import _thread_locals
@@ -45,6 +47,33 @@ def has_xss(value):
return bool(xss_pattern.search(value))
def upload_path(instance, filename):
"""
Generates a unique file path for uploads in the format:
app_label/model_name/field_name/originalfilename-uuid.ext
"""
ext = filename.split(".")[-1]
base_name = ".".join(filename.split(".")[:-1]) or "file"
unique_name = f"{slugify(base_name)}-{uuid4().hex[:8]}.{ext}"
# Try to find which field is uploading this file
field_name = next(
(
k
for k, v in instance.__dict__.items()
if hasattr(v, "name") and v.name == filename
),
None,
)
app_label = instance._meta.app_label
model_name = instance._meta.model_name
if field_name:
return f"{app_label}/{model_name}/{field_name}/{unique_name}"
return f"{app_label}/{model_name}/{unique_name}"
class HorillaModel(models.Model):
"""
An abstract base model that includes common fields and functionalities

View File

@@ -26,7 +26,7 @@ from base.models import (
)
from employee.models import Employee, EmployeeWorkInformation
from horilla import horilla_middlewares
from horilla.models import HorillaModel
from horilla.models import HorillaModel, upload_path
from horilla_audit.methods import get_diff
from horilla_audit.models import HorillaAuditInfo, HorillaAuditLog
from leave.methods import (
@@ -158,7 +158,7 @@ WEEK_DAYS = [
class LeaveType(HorillaModel):
icon = models.ImageField(
null=True, blank=True, upload_to="leave/leave_icon", verbose_name=_("Icon")
null=True, blank=True, upload_to=upload_path, verbose_name=_("Icon")
)
name = models.CharField(max_length=30, null=False, verbose_name=_("Name"))
color = models.CharField(null=True, max_length=30, verbose_name=_("Color"))
@@ -652,7 +652,7 @@ class LeaveRequest(HorillaModel):
attachment = models.FileField(
null=True,
blank=True,
upload_to="leave/leave_attachment",
upload_to=upload_path,
verbose_name=_("Attachment"),
)
status = models.CharField(
@@ -1195,7 +1195,7 @@ class LeaveRequest(HorillaModel):
class LeaverequestFile(models.Model):
file = models.FileField(upload_to="leave/request_files")
file = models.FileField(upload_to=upload_path)
class LeaverequestComment(HorillaModel):
@@ -1227,7 +1227,7 @@ class LeaveAllocationRequest(HorillaModel):
attachment = models.FileField(
null=True,
blank=True,
upload_to="leave/leave_attachment",
upload_to=upload_path,
verbose_name=_("Attachment"),
)
status = models.CharField(

View File

@@ -14,7 +14,7 @@ from employee.models import Employee
from horilla import horilla_middlewares
from horilla.horilla_middlewares import _thread_locals
from horilla.methods import get_horilla_model_class
from horilla.models import HorillaModel
from horilla.models import HorillaModel, upload_path
from horilla_audit.models import HorillaAuditInfo, HorillaAuditLog
from notifications.signals import notify
@@ -111,7 +111,7 @@ class OffboardingStageMultipleFile(HorillaModel):
OffboardingStageMultipleFile
"""
attachment = models.FileField(upload_to="offboarding/attachments")
attachment = models.FileField(upload_to=upload_path)
class OffboardingEmployee(HorillaModel):

View File

@@ -30,7 +30,7 @@ from base.models import (
from employee.methods.duration_methods import strtime_seconds
from employee.models import BonusPoint, Employee, EmployeeWorkInformation
from horilla import horilla_middlewares
from horilla.models import HorillaModel
from horilla.models import HorillaModel, upload_path
from horilla_audit.models import HorillaAuditInfo, HorillaAuditLog
logger = logging.getLogger(__name__)
@@ -242,7 +242,7 @@ class Contract(HorillaModel):
validators=[min_zero],
verbose_name=_("Notice Period"),
)
contract_document = models.FileField(upload_to="uploads/", null=True, blank=True)
contract_document = models.FileField(upload_to=upload_path, null=True, blank=True)
deduct_leave_from_basic_pay = models.BooleanField(
default=True,
verbose_name=_("Deduct From Basic Pay"),
@@ -1570,7 +1570,7 @@ class ReimbursementMultipleAttachment(models.Model):
ReimbursementMultipleAttachement Model
"""
attachment = models.FileField(upload_to="payroll/reimbursements")
attachment = models.FileField(upload_to=upload_path)
objects = models.Manager()
@@ -1600,7 +1600,7 @@ class Reimbursement(HorillaModel):
Employee, on_delete=models.PROTECT, verbose_name="Employee"
)
allowance_on = models.DateField()
attachment = models.FileField(upload_to="payroll/reimbursements", null=True)
attachment = models.FileField(upload_to=upload_path, null=True)
other_attachments = models.ManyToManyField(
ReimbursementMultipleAttachment, blank=True, editable=False
)
@@ -1787,7 +1787,7 @@ class Reimbursement(HorillaModel):
class ReimbursementFile(models.Model):
file = models.FileField(upload_to="payroll/request_files")
file = models.FileField(upload_to=upload_path)
objects = models.Manager()

View File

@@ -22,7 +22,7 @@ from base.models import Company
from employee.models import Employee
from horilla import horilla_middlewares
from horilla.horilla_middlewares import _thread_locals
from horilla.models import HorillaModel
from horilla.models import HorillaModel, upload_path
from horilla_views.cbv_methods import render_template
# Create your models here.
@@ -75,7 +75,7 @@ class Project(HorillaModel):
start_date = models.DateField(verbose_name=_("Start Date"))
end_date = models.DateField(null=True, blank=True, verbose_name=_("End Date"))
document = models.FileField(
upload_to="project/files", blank=True, null=True, verbose_name=_("Project File")
upload_to=upload_path, blank=True, null=True, verbose_name=_("Project File")
)
description = models.TextField(verbose_name=_("Description"))
company_id = models.ForeignKey(
@@ -355,7 +355,7 @@ class Task(HorillaModel):
start_date = models.DateField(null=True, blank=True, verbose_name=_("Start Date"))
end_date = models.DateField(null=True, blank=True, verbose_name=_("End Date"))
document = models.FileField(
upload_to="task/files", blank=True, null=True, verbose_name=_("Task File")
upload_to=upload_path, blank=True, null=True, verbose_name=_("Task File")
)
description = models.TextField(verbose_name=_("Description"))
sequence = models.IntegerField(default=0)

View File

@@ -23,7 +23,7 @@ from django.utils.translation import gettext_lazy as _
from base.horilla_company_manager import HorillaCompanyManager
from base.models import Company, JobPosition
from employee.models import Employee
from horilla.models import HorillaModel
from horilla.models import HorillaModel, upload_path
from horilla_audit.methods import get_diff
from horilla_audit.models import HorillaAuditInfo, HorillaAuditLog
from horilla_views.cbv_methods import render_template
@@ -370,7 +370,7 @@ class Candidate(HorillaModel):
("other", _("Other")),
]
name = models.CharField(max_length=100, null=True, verbose_name=_("Name"))
profile = models.ImageField(upload_to=candidate_upload_path, null=True) # 853
profile = models.ImageField(upload_to=upload_path, null=True) # 853
portfolio = models.URLField(max_length=200, blank=True)
recruitment_id = models.ForeignKey(
Recruitment,
@@ -413,7 +413,7 @@ class Candidate(HorillaModel):
verbose_name=_("Mobile"),
)
resume = models.FileField(
upload_to=candidate_upload_path, # 853
upload_to=upload_path, # 853
validators=[
validate_pdf,
],
@@ -709,7 +709,7 @@ class RejectedCandidate(HorillaModel):
class StageFiles(HorillaModel):
files = models.FileField(upload_to="recruitment/stageFiles", blank=True, null=True)
files = models.FileField(upload_to=upload_path, blank=True, null=True)
def __str__(self):
return self.files.name.split("/")[-1]
@@ -837,9 +837,7 @@ class RecruitmentSurveyAnswer(HorillaModel):
null=True,
)
answer_json = models.JSONField()
attachment = models.FileField(
upload_to="recruitment_attachment", null=True, blank=True
)
attachment = models.FileField(upload_to=upload_path, null=True, blank=True)
objects = HorillaCompanyManager(related_company_field="recruitment_id__company_id")
@property
@@ -1000,7 +998,7 @@ class InterviewSchedule(HorillaModel):
class Resume(models.Model):
file = models.FileField(
upload_to="recruitment/resume",
upload_to=upload_path,
validators=[
validate_pdf,
],
@@ -1054,7 +1052,7 @@ class CandidateDocument(HorillaModel):
document_request_id = models.ForeignKey(
CandidateDocumentRequest, on_delete=models.PROTECT, null=True
)
document = models.FileField(upload_to="candidate/documents", null=True)
document = models.FileField(upload_to=upload_path, null=True)
status = models.CharField(choices=STATUS, max_length=10, default="requested")
reject_reason = models.TextField(blank=True, null=True, max_length=255)