mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-05-19 12:42:14 -05:00
Change: treat created as date not datetime (#9793)
This commit is contained in:
parent
ce5d4e9c92
commit
1a6f32534c
@ -48,6 +48,15 @@ CHAR_KWARGS = ["istartswith", "iendswith", "icontains", "iexact"]
|
|||||||
ID_KWARGS = ["in", "exact"]
|
ID_KWARGS = ["in", "exact"]
|
||||||
INT_KWARGS = ["exact", "gt", "gte", "lt", "lte", "isnull"]
|
INT_KWARGS = ["exact", "gt", "gte", "lt", "lte", "isnull"]
|
||||||
DATE_KWARGS = [
|
DATE_KWARGS = [
|
||||||
|
"year",
|
||||||
|
"month",
|
||||||
|
"day",
|
||||||
|
"gt",
|
||||||
|
"gte",
|
||||||
|
"lt",
|
||||||
|
"lte",
|
||||||
|
]
|
||||||
|
DATETIME_KWARGS = [
|
||||||
"year",
|
"year",
|
||||||
"month",
|
"month",
|
||||||
"day",
|
"day",
|
||||||
@ -731,6 +740,19 @@ class DocumentFilterSet(FilterSet):
|
|||||||
|
|
||||||
mime_type = MimeTypeFilter()
|
mime_type = MimeTypeFilter()
|
||||||
|
|
||||||
|
# Backwards compatibility
|
||||||
|
created__date__gt = Filter(
|
||||||
|
field_name="created",
|
||||||
|
label="Created after",
|
||||||
|
lookup_expr="gt",
|
||||||
|
)
|
||||||
|
|
||||||
|
created__date__lt = Filter(
|
||||||
|
field_name="created",
|
||||||
|
label="Created before",
|
||||||
|
lookup_expr="lt",
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Document
|
model = Document
|
||||||
fields = {
|
fields = {
|
||||||
@ -739,8 +761,8 @@ class DocumentFilterSet(FilterSet):
|
|||||||
"content": CHAR_KWARGS,
|
"content": CHAR_KWARGS,
|
||||||
"archive_serial_number": INT_KWARGS,
|
"archive_serial_number": INT_KWARGS,
|
||||||
"created": DATE_KWARGS,
|
"created": DATE_KWARGS,
|
||||||
"added": DATE_KWARGS,
|
"added": DATETIME_KWARGS,
|
||||||
"modified": DATE_KWARGS,
|
"modified": DATETIME_KWARGS,
|
||||||
"original_filename": CHAR_KWARGS,
|
"original_filename": CHAR_KWARGS,
|
||||||
"checksum": CHAR_KWARGS,
|
"checksum": CHAR_KWARGS,
|
||||||
"correspondent": ["isnull"],
|
"correspondent": ["isnull"],
|
||||||
@ -764,8 +786,8 @@ class ShareLinkFilterSet(FilterSet):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = ShareLink
|
model = ShareLink
|
||||||
fields = {
|
fields = {
|
||||||
"created": DATE_KWARGS,
|
"created": DATETIME_KWARGS,
|
||||||
"expiration": DATE_KWARGS,
|
"expiration": DATETIME_KWARGS,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import math
|
|||||||
from collections import Counter
|
from collections import Counter
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from datetime import time
|
||||||
from datetime import timezone
|
from datetime import timezone
|
||||||
from shutil import rmtree
|
from shutil import rmtree
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
@ -168,7 +169,7 @@ def update_document(writer: AsyncWriter, doc: Document) -> None:
|
|||||||
type=doc.document_type.name if doc.document_type else None,
|
type=doc.document_type.name if doc.document_type else None,
|
||||||
type_id=doc.document_type.id if doc.document_type else None,
|
type_id=doc.document_type.id if doc.document_type else None,
|
||||||
has_type=doc.document_type is not None,
|
has_type=doc.document_type is not None,
|
||||||
created=doc.created,
|
created=datetime.combine(doc.created, time.min),
|
||||||
added=doc.added,
|
added=doc.added,
|
||||||
asn=asn,
|
asn=asn,
|
||||||
modified=doc.modified,
|
modified=doc.modified,
|
||||||
|
62
src/documents/migrations/1067_alter_document_created.py
Normal file
62
src/documents/migrations/1067_alter_document_created.py
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# Generated by Django 5.1.7 on 2025-04-04 01:08
|
||||||
|
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
from django.db import models
|
||||||
|
from django.db.models.functions import TruncDate
|
||||||
|
|
||||||
|
|
||||||
|
def migrate_date(apps, schema_editor):
|
||||||
|
Document = apps.get_model("documents", "Document")
|
||||||
|
queryset = Document.objects.annotate(
|
||||||
|
truncated_created=TruncDate("created"),
|
||||||
|
).values("id", "truncated_created")
|
||||||
|
|
||||||
|
# Batch to avoid loading all objects into memory at once,
|
||||||
|
# which would be problematic for large datasets.
|
||||||
|
batch_size = 500
|
||||||
|
updates = []
|
||||||
|
for item in queryset.iterator(chunk_size=batch_size):
|
||||||
|
updates.append(
|
||||||
|
Document(id=item["id"], created_date=item["truncated_created"]),
|
||||||
|
)
|
||||||
|
if len(updates) >= batch_size:
|
||||||
|
Document.objects.bulk_update(updates, ["created_date"])
|
||||||
|
updates.clear()
|
||||||
|
if updates:
|
||||||
|
Document.objects.bulk_update(updates, ["created_date"])
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("documents", "1066_alter_workflowtrigger_schedule_offset_days"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="document",
|
||||||
|
name="created_date",
|
||||||
|
field=models.DateField(null=True),
|
||||||
|
),
|
||||||
|
migrations.RunPython(migrate_date, reverse_code=migrations.RunPython.noop),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name="document",
|
||||||
|
name="created",
|
||||||
|
),
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name="document",
|
||||||
|
old_name="created_date",
|
||||||
|
new_name="created",
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="document",
|
||||||
|
name="created",
|
||||||
|
field=models.DateField(
|
||||||
|
db_index=True,
|
||||||
|
default=datetime.datetime.today,
|
||||||
|
verbose_name="created",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
@ -213,7 +213,11 @@ class Document(SoftDeleteModel, ModelWithOwner):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
created = models.DateTimeField(_("created"), default=timezone.now, db_index=True)
|
created = models.DateField(
|
||||||
|
_("created"),
|
||||||
|
default=datetime.datetime.today,
|
||||||
|
db_index=True,
|
||||||
|
)
|
||||||
|
|
||||||
modified = models.DateTimeField(
|
modified = models.DateTimeField(
|
||||||
_("modified"),
|
_("modified"),
|
||||||
@ -291,8 +295,7 @@ class Document(SoftDeleteModel, ModelWithOwner):
|
|||||||
verbose_name_plural = _("documents")
|
verbose_name_plural = _("documents")
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
# Convert UTC database time to local time
|
created = self.created.isoformat()
|
||||||
created = datetime.date.isoformat(timezone.localdate(self.created))
|
|
||||||
|
|
||||||
res = f"{created}"
|
res = f"{created}"
|
||||||
|
|
||||||
@ -371,7 +374,7 @@ class Document(SoftDeleteModel, ModelWithOwner):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def created_date(self):
|
def created_date(self):
|
||||||
return timezone.localdate(self.created)
|
return self.created
|
||||||
|
|
||||||
|
|
||||||
class SavedView(ModelWithOwner):
|
class SavedView(ModelWithOwner):
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import datetime
|
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
import re
|
import re
|
||||||
import zoneinfo
|
from datetime import datetime
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
@ -423,7 +422,7 @@ class OwnedObjectListSerializer(serializers.ListSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class CorrespondentSerializer(MatchingModelSerializer, OwnedObjectSerializer):
|
class CorrespondentSerializer(MatchingModelSerializer, OwnedObjectSerializer):
|
||||||
last_correspondence = serializers.DateTimeField(read_only=True, required=False)
|
last_correspondence = serializers.DateField(read_only=True, required=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Correspondent
|
model = Correspondent
|
||||||
@ -966,11 +965,7 @@ class DocumentSerializer(
|
|||||||
|
|
||||||
def update(self, instance: Document, validated_data):
|
def update(self, instance: Document, validated_data):
|
||||||
if "created_date" in validated_data and "created" not in validated_data:
|
if "created_date" in validated_data and "created" not in validated_data:
|
||||||
new_datetime = datetime.datetime.combine(
|
instance.created = validated_data.get("created_date")
|
||||||
validated_data.get("created_date"),
|
|
||||||
datetime.time(0, 0, 0, 0, zoneinfo.ZoneInfo(settings.TIME_ZONE)),
|
|
||||||
)
|
|
||||||
instance.created = new_datetime
|
|
||||||
instance.save()
|
instance.save()
|
||||||
if "created_date" in validated_data:
|
if "created_date" in validated_data:
|
||||||
validated_data.pop("created_date")
|
validated_data.pop("created_date")
|
||||||
@ -1646,6 +1641,11 @@ class PostDocumentSerializer(serializers.Serializer):
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def validate_created(self, created):
|
||||||
|
# support datetime format for created for backwards compatibility
|
||||||
|
if isinstance(created, datetime):
|
||||||
|
return created.date()
|
||||||
|
|
||||||
|
|
||||||
class BulkDownloadSerializer(DocumentListSerializer):
|
class BulkDownloadSerializer(DocumentListSerializer):
|
||||||
content = serializers.ChoiceField(
|
content = serializers.ChoiceField(
|
||||||
|
@ -722,7 +722,7 @@ def run_workflows(
|
|||||||
timezone.localtime(document.added),
|
timezone.localtime(document.added),
|
||||||
document.original_filename or "",
|
document.original_filename or "",
|
||||||
document.filename or "",
|
document.filename or "",
|
||||||
timezone.localtime(document.created),
|
document.created,
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception(
|
logger.exception(
|
||||||
@ -1010,7 +1010,7 @@ def run_workflows(
|
|||||||
filename = document.original_filename or ""
|
filename = document.original_filename or ""
|
||||||
current_filename = document.filename or ""
|
current_filename = document.filename or ""
|
||||||
added = timezone.localtime(document.added)
|
added = timezone.localtime(document.added)
|
||||||
created = timezone.localtime(document.created)
|
created = document.created
|
||||||
else:
|
else:
|
||||||
title = overrides.title if overrides.title else str(document.original_file)
|
title = overrides.title if overrides.title else str(document.original_file)
|
||||||
doc_url = ""
|
doc_url = ""
|
||||||
@ -1032,7 +1032,7 @@ def run_workflows(
|
|||||||
filename = document.original_file if document.original_file else ""
|
filename = document.original_file if document.original_file else ""
|
||||||
current_filename = filename
|
current_filename = filename
|
||||||
added = timezone.localtime(timezone.now())
|
added = timezone.localtime(timezone.now())
|
||||||
created = timezone.localtime(overrides.created)
|
created = overrides.created
|
||||||
|
|
||||||
subject = (
|
subject = (
|
||||||
parse_w_workflow_placeholders(
|
parse_w_workflow_placeholders(
|
||||||
@ -1098,7 +1098,7 @@ def run_workflows(
|
|||||||
filename = document.original_filename or ""
|
filename = document.original_filename or ""
|
||||||
current_filename = document.filename or ""
|
current_filename = document.filename or ""
|
||||||
added = timezone.localtime(document.added)
|
added = timezone.localtime(document.added)
|
||||||
created = timezone.localtime(document.created)
|
created = document.created
|
||||||
else:
|
else:
|
||||||
title = overrides.title if overrides.title else str(document.original_file)
|
title = overrides.title if overrides.title else str(document.original_file)
|
||||||
doc_url = ""
|
doc_url = ""
|
||||||
@ -1120,7 +1120,7 @@ def run_workflows(
|
|||||||
filename = document.original_file if document.original_file else ""
|
filename = document.original_file if document.original_file else ""
|
||||||
current_filename = filename
|
current_filename = filename
|
||||||
added = timezone.localtime(timezone.now())
|
added = timezone.localtime(timezone.now())
|
||||||
created = timezone.localtime(overrides.created)
|
created = overrides.created
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = {}
|
data = {}
|
||||||
|
@ -137,16 +137,14 @@ def get_creation_date_context(document: Document) -> dict[str, str]:
|
|||||||
Given a Document, localizes the creation date and builds a context dictionary with some common, shorthand
|
Given a Document, localizes the creation date and builds a context dictionary with some common, shorthand
|
||||||
formatted values from it
|
formatted values from it
|
||||||
"""
|
"""
|
||||||
local_created = timezone.localdate(document.created)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"created": local_created.isoformat(),
|
"created": document.created.isoformat(),
|
||||||
"created_year": local_created.strftime("%Y"),
|
"created_year": document.created.strftime("%Y"),
|
||||||
"created_year_short": local_created.strftime("%y"),
|
"created_year_short": document.created.strftime("%y"),
|
||||||
"created_month": local_created.strftime("%m"),
|
"created_month": document.created.strftime("%m"),
|
||||||
"created_month_name": local_created.strftime("%B"),
|
"created_month_name": document.created.strftime("%B"),
|
||||||
"created_month_name_short": local_created.strftime("%b"),
|
"created_month_name_short": document.created.strftime("%b"),
|
||||||
"created_day": local_created.strftime("%d"),
|
"created_day": document.created.strftime("%d"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from datetime import date
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@ -10,7 +11,7 @@ def parse_w_workflow_placeholders(
|
|||||||
local_added: datetime,
|
local_added: datetime,
|
||||||
original_filename: str,
|
original_filename: str,
|
||||||
filename: str,
|
filename: str,
|
||||||
created: datetime | None = None,
|
created: date | None = None,
|
||||||
doc_title: str | None = None,
|
doc_title: str | None = None,
|
||||||
doc_url: str | None = None,
|
doc_url: str | None = None,
|
||||||
) -> str:
|
) -> str:
|
||||||
|
@ -171,6 +171,32 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase):
|
|||||||
results = response.data["results"]
|
results = response.data["results"]
|
||||||
self.assertEqual(len(results[0]), 0)
|
self.assertEqual(len(results[0]), 0)
|
||||||
|
|
||||||
|
def test_document_update_with_created_date(self):
|
||||||
|
"""
|
||||||
|
GIVEN:
|
||||||
|
- Existing document
|
||||||
|
WHEN:
|
||||||
|
- Document is updated with created_date and not created
|
||||||
|
THEN:
|
||||||
|
- Document created field is updated
|
||||||
|
"""
|
||||||
|
doc = Document.objects.create(
|
||||||
|
title="none",
|
||||||
|
checksum="123",
|
||||||
|
mime_type="application/pdf",
|
||||||
|
created=date(2023, 1, 1),
|
||||||
|
)
|
||||||
|
|
||||||
|
created_date = date(2023, 2, 1)
|
||||||
|
self.client.patch(
|
||||||
|
f"/api/documents/{doc.pk}/",
|
||||||
|
{"created_date": created_date},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
|
||||||
|
doc.refresh_from_db()
|
||||||
|
self.assertEqual(doc.created_date, created_date)
|
||||||
|
|
||||||
def test_document_actions(self):
|
def test_document_actions(self):
|
||||||
_, filename = tempfile.mkstemp(dir=self.dirs.originals_dir)
|
_, filename = tempfile.mkstemp(dir=self.dirs.originals_dir)
|
||||||
|
|
||||||
@ -1313,7 +1339,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase):
|
|||||||
|
|
||||||
_, overrides = self.get_last_consume_delay_call_args()
|
_, overrides = self.get_last_consume_delay_call_args()
|
||||||
|
|
||||||
self.assertEqual(overrides.created, created)
|
self.assertEqual(overrides.created, created.date())
|
||||||
|
|
||||||
def test_upload_with_asn(self):
|
def test_upload_with_asn(self):
|
||||||
self.consume_file_mock.return_value = celery.result.AsyncResult(
|
self.consume_file_mock.return_value = celery.result.AsyncResult(
|
||||||
|
@ -4,7 +4,6 @@ from unittest import mock
|
|||||||
|
|
||||||
from django.contrib.auth.models import Permission
|
from django.contrib.auth.models import Permission
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.utils import timezone
|
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.test import APITestCase
|
from rest_framework.test import APITestCase
|
||||||
|
|
||||||
@ -104,13 +103,13 @@ class TestApiObjects(DirectoriesMixin, APITestCase):
|
|||||||
Document.objects.create(
|
Document.objects.create(
|
||||||
mime_type="application/pdf",
|
mime_type="application/pdf",
|
||||||
correspondent=self.c1,
|
correspondent=self.c1,
|
||||||
created=timezone.make_aware(datetime.datetime(2022, 1, 1)),
|
created=datetime.date(2022, 1, 1),
|
||||||
checksum="123",
|
checksum="123",
|
||||||
)
|
)
|
||||||
Document.objects.create(
|
Document.objects.create(
|
||||||
mime_type="application/pdf",
|
mime_type="application/pdf",
|
||||||
correspondent=self.c1,
|
correspondent=self.c1,
|
||||||
created=timezone.make_aware(datetime.datetime(2022, 1, 2)),
|
created=datetime.date(2022, 1, 2),
|
||||||
checksum="456",
|
checksum="456",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -474,7 +474,7 @@ class TestApiAuth(DirectoriesMixin, APITestCase):
|
|||||||
self.client.force_authenticate(user1)
|
self.client.force_authenticate(user1)
|
||||||
|
|
||||||
response = self.client.get(
|
response = self.client.get(
|
||||||
"/api/documents/",
|
"/api/documents/?ordering=-id",
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -721,7 +721,7 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase):
|
|||||||
d3.tags.add(t2)
|
d3.tags.add(t2)
|
||||||
d4 = Document.objects.create(
|
d4 = Document.objects.create(
|
||||||
checksum="4",
|
checksum="4",
|
||||||
created=timezone.make_aware(datetime.datetime(2020, 7, 13)),
|
created=datetime.date(2020, 7, 13),
|
||||||
content="test",
|
content="test",
|
||||||
original_filename="doc4.pdf",
|
original_filename="doc4.pdf",
|
||||||
)
|
)
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
from datetime import date
|
||||||
|
|
||||||
from django.contrib.auth.models import Permission
|
from django.contrib.auth.models import Permission
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
@ -116,6 +118,7 @@ class TestTrashAPI(APITestCase):
|
|||||||
checksum="checksum",
|
checksum="checksum",
|
||||||
mime_type="application/pdf",
|
mime_type="application/pdf",
|
||||||
owner=self.user,
|
owner=self.user,
|
||||||
|
created=date(2023, 1, 1),
|
||||||
)
|
)
|
||||||
document_u1.delete()
|
document_u1.delete()
|
||||||
document_not_owned = Document.objects.create(
|
document_not_owned = Document.objects.create(
|
||||||
@ -123,6 +126,7 @@ class TestTrashAPI(APITestCase):
|
|||||||
content="content2",
|
content="content2",
|
||||||
checksum="checksum2",
|
checksum="checksum2",
|
||||||
mime_type="application/pdf",
|
mime_type="application/pdf",
|
||||||
|
created=date(2023, 1, 2),
|
||||||
)
|
)
|
||||||
document_not_owned.delete()
|
document_not_owned.delete()
|
||||||
user2 = User.objects.create_user(username="user2")
|
user2 = User.objects.create_user(username="user2")
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import shutil
|
import shutil
|
||||||
|
from datetime import date
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
@ -39,18 +40,24 @@ class TestBulkEdit(DirectoriesMixin, TestCase):
|
|||||||
self.dt2 = DocumentType.objects.create(name="dt2")
|
self.dt2 = DocumentType.objects.create(name="dt2")
|
||||||
self.t1 = Tag.objects.create(name="t1")
|
self.t1 = Tag.objects.create(name="t1")
|
||||||
self.t2 = Tag.objects.create(name="t2")
|
self.t2 = Tag.objects.create(name="t2")
|
||||||
self.doc1 = Document.objects.create(checksum="A", title="A")
|
self.doc1 = Document.objects.create(
|
||||||
|
checksum="A",
|
||||||
|
title="A",
|
||||||
|
created=date(2023, 1, 1),
|
||||||
|
)
|
||||||
self.doc2 = Document.objects.create(
|
self.doc2 = Document.objects.create(
|
||||||
checksum="B",
|
checksum="B",
|
||||||
title="B",
|
title="B",
|
||||||
correspondent=self.c1,
|
correspondent=self.c1,
|
||||||
document_type=self.dt1,
|
document_type=self.dt1,
|
||||||
|
created=date(2023, 1, 2),
|
||||||
)
|
)
|
||||||
self.doc3 = Document.objects.create(
|
self.doc3 = Document.objects.create(
|
||||||
checksum="C",
|
checksum="C",
|
||||||
title="C",
|
title="C",
|
||||||
correspondent=self.c2,
|
correspondent=self.c2,
|
||||||
document_type=self.dt2,
|
document_type=self.dt2,
|
||||||
|
created=date(2023, 1, 3),
|
||||||
)
|
)
|
||||||
self.doc4 = Document.objects.create(checksum="D", title="D")
|
self.doc4 = Document.objects.create(checksum="D", title="D")
|
||||||
self.doc5 = Document.objects.create(checksum="E", title="E")
|
self.doc5 = Document.objects.create(checksum="E", title="E")
|
||||||
@ -529,6 +536,7 @@ class TestPDFActions(DirectoriesMixin, TestCase):
|
|||||||
filename=sample2,
|
filename=sample2,
|
||||||
mime_type="application/pdf",
|
mime_type="application/pdf",
|
||||||
page_count=8,
|
page_count=8,
|
||||||
|
created=date(2023, 1, 2),
|
||||||
)
|
)
|
||||||
self.doc2.archive_filename = sample2_archive
|
self.doc2.archive_filename = sample2_archive
|
||||||
self.doc2.save()
|
self.doc2.save()
|
||||||
@ -557,6 +565,7 @@ class TestPDFActions(DirectoriesMixin, TestCase):
|
|||||||
title="D",
|
title="D",
|
||||||
filename=img_doc,
|
filename=img_doc,
|
||||||
mime_type="image/jpeg",
|
mime_type="image/jpeg",
|
||||||
|
created=date(2023, 1, 3),
|
||||||
)
|
)
|
||||||
self.img_doc.archive_filename = img_doc_archive
|
self.img_doc.archive_filename = img_doc_archive
|
||||||
self.img_doc.save()
|
self.img_doc.save()
|
||||||
|
@ -3,12 +3,10 @@ import os
|
|||||||
import shutil
|
import shutil
|
||||||
import stat
|
import stat
|
||||||
import tempfile
|
import tempfile
|
||||||
import zoneinfo
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
from dateutil import tz
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import Group
|
from django.contrib.auth.models import Group
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
@ -247,20 +245,9 @@ class TestConsumer(
|
|||||||
|
|
||||||
self._assert_first_last_send_progress()
|
self._assert_first_last_send_progress()
|
||||||
|
|
||||||
# Convert UTC time from DB to local time
|
self.assertEqual(document.created.year, rough_create_date_local.year)
|
||||||
document_date_local = timezone.localtime(document.created)
|
self.assertEqual(document.created.month, rough_create_date_local.month)
|
||||||
|
self.assertEqual(document.created.day, rough_create_date_local.day)
|
||||||
self.assertEqual(
|
|
||||||
document_date_local.tzinfo,
|
|
||||||
zoneinfo.ZoneInfo("America/Chicago"),
|
|
||||||
)
|
|
||||||
self.assertEqual(document_date_local.tzinfo, rough_create_date_local.tzinfo)
|
|
||||||
self.assertEqual(document_date_local.year, rough_create_date_local.year)
|
|
||||||
self.assertEqual(document_date_local.month, rough_create_date_local.month)
|
|
||||||
self.assertEqual(document_date_local.day, rough_create_date_local.day)
|
|
||||||
self.assertEqual(document_date_local.hour, rough_create_date_local.hour)
|
|
||||||
self.assertEqual(document_date_local.minute, rough_create_date_local.minute)
|
|
||||||
# Skipping seconds and more precise
|
|
||||||
|
|
||||||
@override_settings(FILENAME_FORMAT=None)
|
@override_settings(FILENAME_FORMAT=None)
|
||||||
def testDeleteMacFiles(self):
|
def testDeleteMacFiles(self):
|
||||||
@ -931,7 +918,7 @@ class TestConsumerCreatedDate(DirectoriesMixin, GetConsumerMixin, TestCase):
|
|||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
document.created,
|
document.created,
|
||||||
datetime.datetime(1996, 2, 20, tzinfo=tz.gettz(settings.TIME_ZONE)),
|
datetime.date(1996, 2, 20),
|
||||||
)
|
)
|
||||||
|
|
||||||
@override_settings(FILENAME_DATE_ORDER="YMD")
|
@override_settings(FILENAME_DATE_ORDER="YMD")
|
||||||
@ -961,7 +948,7 @@ class TestConsumerCreatedDate(DirectoriesMixin, GetConsumerMixin, TestCase):
|
|||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
document.created,
|
document.created,
|
||||||
datetime.datetime(2022, 2, 1, tzinfo=tz.gettz(settings.TIME_ZONE)),
|
datetime.date(2022, 2, 1),
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_consume_date_filename_date_use_content(self):
|
def test_consume_date_filename_date_use_content(self):
|
||||||
@ -991,7 +978,7 @@ class TestConsumerCreatedDate(DirectoriesMixin, GetConsumerMixin, TestCase):
|
|||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
document.created,
|
document.created,
|
||||||
datetime.datetime(1996, 2, 20, tzinfo=tz.gettz(settings.TIME_ZONE)),
|
datetime.date(1996, 2, 20),
|
||||||
)
|
)
|
||||||
|
|
||||||
@override_settings(
|
@override_settings(
|
||||||
@ -1023,7 +1010,7 @@ class TestConsumerCreatedDate(DirectoriesMixin, GetConsumerMixin, TestCase):
|
|||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
document.created,
|
document.created,
|
||||||
datetime.datetime(1997, 2, 20, tzinfo=tz.gettz(settings.TIME_ZONE)),
|
datetime.date(1997, 2, 20),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
import shutil
|
import shutil
|
||||||
import tempfile
|
import tempfile
|
||||||
import zoneinfo
|
from datetime import date
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.test import override_settings
|
from django.test import override_settings
|
||||||
from django.utils import timezone
|
|
||||||
|
|
||||||
from documents.models import Correspondent
|
from documents.models import Correspondent
|
||||||
from documents.models import Document
|
from documents.models import Document
|
||||||
@ -81,60 +80,15 @@ class TestDocument(TestCase):
|
|||||||
doc = Document(
|
doc = Document(
|
||||||
mime_type="application/pdf",
|
mime_type="application/pdf",
|
||||||
title="test",
|
title="test",
|
||||||
created=timezone.datetime(2020, 12, 25, tzinfo=zoneinfo.ZoneInfo("UTC")),
|
created=date(2020, 12, 25),
|
||||||
)
|
)
|
||||||
self.assertEqual(doc.get_public_filename(), "2020-12-25 test.pdf")
|
self.assertEqual(doc.get_public_filename(), "2020-12-25 test.pdf")
|
||||||
|
|
||||||
@override_settings(
|
|
||||||
TIME_ZONE="Europe/Berlin",
|
|
||||||
)
|
|
||||||
def test_file_name_with_timezone(self):
|
|
||||||
# See https://docs.djangoproject.com/en/4.0/ref/utils/#django.utils.timezone.now
|
|
||||||
# The default for created is an aware datetime in UTC
|
|
||||||
# This does that, just manually, with a fixed date
|
|
||||||
local_create_date = timezone.datetime(
|
|
||||||
2020,
|
|
||||||
12,
|
|
||||||
25,
|
|
||||||
tzinfo=zoneinfo.ZoneInfo("Europe/Berlin"),
|
|
||||||
)
|
|
||||||
|
|
||||||
utc_create_date = local_create_date.astimezone(zoneinfo.ZoneInfo("UTC"))
|
|
||||||
|
|
||||||
doc = Document(
|
|
||||||
mime_type="application/pdf",
|
|
||||||
title="test",
|
|
||||||
created=utc_create_date,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Ensure the create date would cause an off by 1 if not properly created above
|
|
||||||
self.assertEqual(utc_create_date.date().day, 24)
|
|
||||||
self.assertEqual(doc.get_public_filename(), "2020-12-25 test.pdf")
|
|
||||||
|
|
||||||
local_create_date = timezone.datetime(
|
|
||||||
2020,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
tzinfo=zoneinfo.ZoneInfo("Europe/Berlin"),
|
|
||||||
)
|
|
||||||
|
|
||||||
utc_create_date = local_create_date.astimezone(zoneinfo.ZoneInfo("UTC"))
|
|
||||||
|
|
||||||
doc = Document(
|
|
||||||
mime_type="application/pdf",
|
|
||||||
title="test",
|
|
||||||
created=utc_create_date,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Ensure the create date would cause an off by 1 in the year if not properly created above
|
|
||||||
self.assertEqual(utc_create_date.date().year, 2019)
|
|
||||||
self.assertEqual(doc.get_public_filename(), "2020-01-01 test.pdf")
|
|
||||||
|
|
||||||
def test_file_name_jpg(self):
|
def test_file_name_jpg(self):
|
||||||
doc = Document(
|
doc = Document(
|
||||||
mime_type="image/jpeg",
|
mime_type="image/jpeg",
|
||||||
title="test",
|
title="test",
|
||||||
created=timezone.datetime(2020, 12, 25, tzinfo=zoneinfo.ZoneInfo("UTC")),
|
created=date(2020, 12, 25),
|
||||||
)
|
)
|
||||||
self.assertEqual(doc.get_public_filename(), "2020-12-25 test.jpg")
|
self.assertEqual(doc.get_public_filename(), "2020-12-25 test.jpg")
|
||||||
|
|
||||||
@ -142,7 +96,7 @@ class TestDocument(TestCase):
|
|||||||
doc = Document(
|
doc = Document(
|
||||||
mime_type="application/zip",
|
mime_type="application/zip",
|
||||||
title="test",
|
title="test",
|
||||||
created=timezone.datetime(2020, 12, 25, tzinfo=zoneinfo.ZoneInfo("UTC")),
|
created=date(2020, 12, 25),
|
||||||
)
|
)
|
||||||
self.assertEqual(doc.get_public_filename(), "2020-12-25 test.zip")
|
self.assertEqual(doc.get_public_filename(), "2020-12-25 test.zip")
|
||||||
|
|
||||||
@ -150,6 +104,6 @@ class TestDocument(TestCase):
|
|||||||
doc = Document(
|
doc = Document(
|
||||||
mime_type="image/jpegasd",
|
mime_type="image/jpegasd",
|
||||||
title="test",
|
title="test",
|
||||||
created=timezone.datetime(2020, 12, 25, tzinfo=zoneinfo.ZoneInfo("UTC")),
|
created=date(2020, 12, 25),
|
||||||
)
|
)
|
||||||
self.assertEqual(doc.get_public_filename(), "2020-12-25 test")
|
self.assertEqual(doc.get_public_filename(), "2020-12-25 test")
|
||||||
|
@ -333,7 +333,7 @@ class TestFileHandling(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
|
|||||||
|
|
||||||
self.assertEqual(generate_filename(doc1), "2020-03-06.pdf")
|
self.assertEqual(generate_filename(doc1), "2020-03-06.pdf")
|
||||||
|
|
||||||
doc1.created = timezone.make_aware(datetime.datetime(2020, 11, 16, 1, 1, 1))
|
doc1.created = datetime.date(2020, 11, 16)
|
||||||
|
|
||||||
self.assertEqual(generate_filename(doc1), "2020-11-16.pdf")
|
self.assertEqual(generate_filename(doc1), "2020-11-16.pdf")
|
||||||
|
|
||||||
@ -912,7 +912,7 @@ class TestFilenameGeneration(DirectoriesMixin, TestCase):
|
|||||||
def test_date(self):
|
def test_date(self):
|
||||||
doc = Document.objects.create(
|
doc = Document.objects.create(
|
||||||
title="does not matter",
|
title="does not matter",
|
||||||
created=timezone.make_aware(datetime.datetime(2020, 5, 21, 7, 36, 51, 153)),
|
created=datetime.date(2020, 5, 21),
|
||||||
mime_type="application/pdf",
|
mime_type="application/pdf",
|
||||||
pk=2,
|
pk=2,
|
||||||
checksum="2",
|
checksum="2",
|
||||||
@ -930,7 +930,7 @@ class TestFilenameGeneration(DirectoriesMixin, TestCase):
|
|||||||
"""
|
"""
|
||||||
doc = Document.objects.create(
|
doc = Document.objects.create(
|
||||||
title="does not matter",
|
title="does not matter",
|
||||||
created=timezone.make_aware(datetime.datetime(2020, 6, 25, 7, 36, 51, 153)),
|
created=datetime.date(2020, 6, 25),
|
||||||
mime_type="application/pdf",
|
mime_type="application/pdf",
|
||||||
pk=2,
|
pk=2,
|
||||||
checksum="2",
|
checksum="2",
|
||||||
@ -951,7 +951,7 @@ class TestFilenameGeneration(DirectoriesMixin, TestCase):
|
|||||||
"""
|
"""
|
||||||
doc = Document.objects.create(
|
doc = Document.objects.create(
|
||||||
title="does not matter",
|
title="does not matter",
|
||||||
created=timezone.make_aware(datetime.datetime(2020, 6, 25, 7, 36, 51, 153)),
|
created=datetime.date(2020, 6, 25),
|
||||||
mime_type="application/pdf",
|
mime_type="application/pdf",
|
||||||
pk=2,
|
pk=2,
|
||||||
checksum="2",
|
checksum="2",
|
||||||
@ -979,7 +979,7 @@ class TestFilenameGeneration(DirectoriesMixin, TestCase):
|
|||||||
)
|
)
|
||||||
doc = Document.objects.create(
|
doc = Document.objects.create(
|
||||||
title="does not matter",
|
title="does not matter",
|
||||||
created=timezone.make_aware(datetime.datetime(2020, 6, 25, 7, 36, 51, 153)),
|
created=datetime.date(2020, 6, 25),
|
||||||
mime_type="application/pdf",
|
mime_type="application/pdf",
|
||||||
pk=2,
|
pk=2,
|
||||||
checksum="2",
|
checksum="2",
|
||||||
@ -1007,7 +1007,7 @@ class TestFilenameGeneration(DirectoriesMixin, TestCase):
|
|||||||
"""
|
"""
|
||||||
doc_a = Document.objects.create(
|
doc_a = Document.objects.create(
|
||||||
title="does not matter",
|
title="does not matter",
|
||||||
created=timezone.make_aware(datetime.datetime(2020, 6, 25, 7, 36, 51, 153)),
|
created=datetime.date(2020, 6, 25),
|
||||||
mime_type="application/pdf",
|
mime_type="application/pdf",
|
||||||
pk=2,
|
pk=2,
|
||||||
checksum="2",
|
checksum="2",
|
||||||
@ -1019,7 +1019,7 @@ class TestFilenameGeneration(DirectoriesMixin, TestCase):
|
|||||||
)
|
)
|
||||||
doc_b = Document.objects.create(
|
doc_b = Document.objects.create(
|
||||||
title="does not matter",
|
title="does not matter",
|
||||||
created=timezone.make_aware(datetime.datetime(2020, 7, 25, 7, 36, 51, 153)),
|
created=datetime.date(2020, 7, 25),
|
||||||
mime_type="application/pdf",
|
mime_type="application/pdf",
|
||||||
pk=5,
|
pk=5,
|
||||||
checksum="abcde",
|
checksum="abcde",
|
||||||
@ -1047,7 +1047,7 @@ class TestFilenameGeneration(DirectoriesMixin, TestCase):
|
|||||||
"""
|
"""
|
||||||
doc_a = Document.objects.create(
|
doc_a = Document.objects.create(
|
||||||
title="does not matter",
|
title="does not matter",
|
||||||
created=timezone.make_aware(datetime.datetime(2020, 6, 25, 7, 36, 51, 153)),
|
created=datetime.date(2020, 6, 25),
|
||||||
mime_type="application/pdf",
|
mime_type="application/pdf",
|
||||||
pk=2,
|
pk=2,
|
||||||
checksum="2",
|
checksum="2",
|
||||||
@ -1055,7 +1055,7 @@ class TestFilenameGeneration(DirectoriesMixin, TestCase):
|
|||||||
)
|
)
|
||||||
doc_b = Document.objects.create(
|
doc_b = Document.objects.create(
|
||||||
title="does not matter",
|
title="does not matter",
|
||||||
created=timezone.make_aware(datetime.datetime(2020, 7, 25, 7, 36, 51, 153)),
|
created=datetime.date(2020, 7, 25),
|
||||||
mime_type="application/pdf",
|
mime_type="application/pdf",
|
||||||
pk=5,
|
pk=5,
|
||||||
checksum="abcde",
|
checksum="abcde",
|
||||||
@ -1074,9 +1074,7 @@ class TestFilenameGeneration(DirectoriesMixin, TestCase):
|
|||||||
def test_short_names_created(self):
|
def test_short_names_created(self):
|
||||||
doc = Document.objects.create(
|
doc = Document.objects.create(
|
||||||
title="The Title",
|
title="The Title",
|
||||||
created=timezone.make_aware(
|
created=datetime.date(1989, 12, 2),
|
||||||
datetime.datetime(1989, 12, 21, 7, 36, 51, 153),
|
|
||||||
),
|
|
||||||
mime_type="application/pdf",
|
mime_type="application/pdf",
|
||||||
pk=2,
|
pk=2,
|
||||||
checksum="2",
|
checksum="2",
|
||||||
@ -1236,7 +1234,7 @@ class TestFilenameGeneration(DirectoriesMixin, TestCase):
|
|||||||
|
|
||||||
doc_a = Document.objects.create(
|
doc_a = Document.objects.create(
|
||||||
title="Does Matter",
|
title="Does Matter",
|
||||||
created=timezone.make_aware(datetime.datetime(2020, 6, 25, 7, 36, 51, 153)),
|
created=datetime.date(2020, 6, 25),
|
||||||
added=timezone.make_aware(datetime.datetime(2024, 10, 1, 7, 36, 51, 153)),
|
added=timezone.make_aware(datetime.datetime(2024, 10, 1, 7, 36, 51, 153)),
|
||||||
mime_type="application/pdf",
|
mime_type="application/pdf",
|
||||||
pk=2,
|
pk=2,
|
||||||
@ -1302,7 +1300,7 @@ class TestFilenameGeneration(DirectoriesMixin, TestCase):
|
|||||||
"""
|
"""
|
||||||
doc_a = Document.objects.create(
|
doc_a = Document.objects.create(
|
||||||
title="Does Matter",
|
title="Does Matter",
|
||||||
created=timezone.make_aware(datetime.datetime(2020, 6, 25, 7, 36, 51, 153)),
|
created=datetime.date(2020, 6, 25),
|
||||||
added=timezone.make_aware(datetime.datetime(2024, 10, 1, 7, 36, 51, 153)),
|
added=timezone.make_aware(datetime.datetime(2024, 10, 1, 7, 36, 51, 153)),
|
||||||
mime_type="application/pdf",
|
mime_type="application/pdf",
|
||||||
pk=2,
|
pk=2,
|
||||||
@ -1337,7 +1335,7 @@ class TestFilenameGeneration(DirectoriesMixin, TestCase):
|
|||||||
"""
|
"""
|
||||||
doc_a = Document.objects.create(
|
doc_a = Document.objects.create(
|
||||||
title="Does Matter",
|
title="Does Matter",
|
||||||
created=timezone.make_aware(datetime.datetime(2020, 6, 25, 7, 36, 51, 153)),
|
created=datetime.date(2020, 6, 25),
|
||||||
added=timezone.make_aware(datetime.datetime(2024, 10, 1, 7, 36, 51, 153)),
|
added=timezone.make_aware(datetime.datetime(2024, 10, 1, 7, 36, 51, 153)),
|
||||||
mime_type="application/pdf",
|
mime_type="application/pdf",
|
||||||
pk=2,
|
pk=2,
|
||||||
@ -1369,7 +1367,7 @@ class TestFilenameGeneration(DirectoriesMixin, TestCase):
|
|||||||
"""
|
"""
|
||||||
doc_a = Document.objects.create(
|
doc_a = Document.objects.create(
|
||||||
title="Some Title",
|
title="Some Title",
|
||||||
created=timezone.make_aware(datetime.datetime(2020, 6, 25, 7, 36, 51, 153)),
|
created=datetime.date(2020, 6, 25),
|
||||||
added=timezone.make_aware(datetime.datetime(2024, 10, 1, 7, 36, 51, 153)),
|
added=timezone.make_aware(datetime.datetime(2024, 10, 1, 7, 36, 51, 153)),
|
||||||
mime_type="application/pdf",
|
mime_type="application/pdf",
|
||||||
pk=2,
|
pk=2,
|
||||||
@ -1474,7 +1472,7 @@ class TestFilenameGeneration(DirectoriesMixin, TestCase):
|
|||||||
"""
|
"""
|
||||||
doc_a = Document.objects.create(
|
doc_a = Document.objects.create(
|
||||||
title="Some Title",
|
title="Some Title",
|
||||||
created=timezone.make_aware(datetime.datetime(2020, 6, 25, 7, 36, 51, 153)),
|
created=datetime.date(2020, 6, 25),
|
||||||
added=timezone.make_aware(datetime.datetime(2024, 10, 1, 7, 36, 51, 153)),
|
added=timezone.make_aware(datetime.datetime(2024, 10, 1, 7, 36, 51, 153)),
|
||||||
mime_type="application/pdf",
|
mime_type="application/pdf",
|
||||||
pk=2,
|
pk=2,
|
||||||
@ -1529,7 +1527,7 @@ class TestFilenameGeneration(DirectoriesMixin, TestCase):
|
|||||||
"""
|
"""
|
||||||
doc = Document.objects.create(
|
doc = Document.objects.create(
|
||||||
title="Some Title! With @ Special # Characters",
|
title="Some Title! With @ Special # Characters",
|
||||||
created=timezone.make_aware(datetime.datetime(2020, 6, 25, 7, 36, 51, 153)),
|
created=datetime.date(2020, 6, 25),
|
||||||
added=timezone.make_aware(datetime.datetime(2024, 10, 1, 7, 36, 51, 153)),
|
added=timezone.make_aware(datetime.datetime(2024, 10, 1, 7, 36, 51, 153)),
|
||||||
mime_type="application/pdf",
|
mime_type="application/pdf",
|
||||||
pk=2,
|
pk=2,
|
||||||
|
36
src/documents/tests/test_migration_created.py
Normal file
36
src/documents/tests/test_migration_created.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
from documents.tests.utils import DirectoriesMixin
|
||||||
|
from documents.tests.utils import TestMigrations
|
||||||
|
|
||||||
|
|
||||||
|
class TestMigrateDocumentCreated(DirectoriesMixin, TestMigrations):
|
||||||
|
migrate_from = "1066_alter_workflowtrigger_schedule_offset_days"
|
||||||
|
migrate_to = "1067_alter_document_created"
|
||||||
|
|
||||||
|
def setUpBeforeMigration(self, apps):
|
||||||
|
# create 600 documents
|
||||||
|
for i in range(600):
|
||||||
|
Document = apps.get_model("documents", "Document")
|
||||||
|
Document.objects.create(
|
||||||
|
title=f"test{i}",
|
||||||
|
mime_type="application/pdf",
|
||||||
|
filename=f"file{i}.pdf",
|
||||||
|
created=datetime(
|
||||||
|
2023,
|
||||||
|
10,
|
||||||
|
1,
|
||||||
|
12,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
+ timedelta(days=i),
|
||||||
|
checksum=i,
|
||||||
|
)
|
||||||
|
|
||||||
|
def testDocumentCreatedMigrated(self):
|
||||||
|
Document = self.apps.get_model("documents", "Document")
|
||||||
|
|
||||||
|
doc = Document.objects.get(id=1)
|
||||||
|
self.assertEqual(doc.created, datetime(2023, 10, 1, 12, 0, 0).date())
|
Loading…
x
Reference in New Issue
Block a user