diff --git a/geonode/api/tests.py b/geonode/api/tests.py index 71559cbe835..c7160fdd637 100644 --- a/geonode/api/tests.py +++ b/geonode/api/tests.py @@ -43,6 +43,7 @@ ThesaurusKeyword, ThesaurusKeywordLabel, ResourceBase, + License, ) from geonode.utils import check_ogc_backend from geonode.decorators import on_ogc_backend @@ -1048,7 +1049,7 @@ def _get_updated_metadata(self, resource, old_owner, new_owner): return { "date_type": "publication", "hkeywords": [], - "license": {"id": "not_specified", "label": "Not Specified"}, + "license": {"id": License.objects.get(identifier="not_specified").id, "label": "Not Specified"}, "regions": [], "supplemental_information": "No information provided", "linkedresources": [], diff --git a/geonode/base/migrations/0098_alter_license_identifier.py b/geonode/base/migrations/0098_alter_license_identifier.py new file mode 100644 index 00000000000..5f2dcd3b9ac --- /dev/null +++ b/geonode/base/migrations/0098_alter_license_identifier.py @@ -0,0 +1,32 @@ +# Generated by Django 5.2.12 on 2026-04-08 06:35 + +from django.db import migrations, models +from django.db.models import Q +import uuid + +def populate_license_identifiers(apps, schema_editor): + License = apps.get_model("base", "License") + seen = set() + for lic in License.objects.order_by("id"): + identifier = lic.identifier + if not identifier or identifier in seen: + lic.identifier = str(uuid.uuid4()) + lic.save(update_fields=["identifier"]) + seen.add(lic.identifier) + else: + seen.add(identifier) + +class Migration(migrations.Migration): + + dependencies = [ + ("base", "0097_alter_link_asset"), + ] + + operations = [ + migrations.RunPython(populate_license_identifiers, migrations.RunPython.noop), + migrations.AlterField( + model_name="license", + name="identifier", + field=models.CharField(max_length=255, unique=True), + ), + ] diff --git a/geonode/base/models.py b/geonode/base/models.py index 4d7bd0f005b..9469871ae26 100644 --- a/geonode/base/models.py +++ b/geonode/base/models.py @@ -225,7 +225,7 @@ class Meta: class License(models.Model): - identifier = models.CharField(max_length=255, editable=False) + identifier = models.CharField(max_length=255, unique=True) name = models.CharField(max_length=255) abbreviation = models.CharField(max_length=20, null=True, blank=True) description = models.TextField(null=True, blank=True) diff --git a/geonode/metadata/api/views.py b/geonode/metadata/api/views.py index 6009893d0fc..2c5ae6e8bd0 100644 --- a/geonode/metadata/api/views.py +++ b/geonode/metadata/api/views.py @@ -296,7 +296,7 @@ def licenses_autocomplete(request: WSGIRequest): for record in qs.all(): ret.append( { - "id": record.identifier, + "id": record.id, "label": _(record.name), } ) diff --git a/geonode/metadata/handlers/base.py b/geonode/metadata/handlers/base.py index c560f631d13..589b9aad828 100644 --- a/geonode/metadata/handlers/base.py +++ b/geonode/metadata/handlers/base.py @@ -115,14 +115,14 @@ def serialize(cls, db_value): if db_value is None: return None elif isinstance(db_value, License): - return {"id": db_value.identifier, "label": _(db_value.name)} + return {"id": db_value.id, "label": _(db_value.name)} else: logger.warning(f"License: can't decode <{type(db_value)}>'{db_value}'") return None @classmethod def deserialize(cls, field_value): - return License.objects.get(identifier=field_value["id"]) if field_value else None + return License.objects.get(id=field_value["id"]) if field_value else None class RestrictionsSubHandler(SubHandler): diff --git a/geonode/metadata/schemas/base.json b/geonode/metadata/schemas/base.json index f268ae69cfa..f022662ecfb 100644 --- a/geonode/metadata/schemas/base.json +++ b/geonode/metadata/schemas/base.json @@ -67,7 +67,7 @@ "maxLength": 255, "properties": { "id": { - "type": "string" + "type": "integer" }, "label": { "type": "string" diff --git a/geonode/metadata/tests/test_handlers.py b/geonode/metadata/tests/test_handlers.py index 2a492f82406..7ce59cd8289 100644 --- a/geonode/metadata/tests/test_handlers.py +++ b/geonode/metadata/tests/test_handlers.py @@ -457,12 +457,12 @@ def test_license_subhandler_serialize_with_existed_db_value(self): # Test the case where the db_value is a model instance serialized_value = LicenseSubHandler.serialize(self.license) - expected_value = {"id": self.license.identifier, "label": _(self.license.name)} + expected_value = {"id": self.license.id, "label": _(self.license.name)} self.assertEqual(serialized_value, expected_value) - # Assert that the serialize method returns the identifier - self.assertEqual(serialized_value["id"], self.license.identifier) + # Assert that the serialize method returns the id + self.assertEqual(serialized_value["id"], self.license.id) def test_license_subhandler_serialize_invalid_data(self): """ @@ -486,14 +486,14 @@ def test_license_subhandler_deserialize(self): An instance of this model has been created initial setup """ - field_value = {"id": "fake_license"} + field_value = {"id": self.license.id} # Call the method using the "fake_category" identifier from the created instance deserialized_value = LicenseSubHandler.deserialize(field_value) # Assert that the deserialized value is the correct model instance self.assertEqual(deserialized_value, self.license) - self.assertEqual(deserialized_value.identifier, field_value["id"]) + self.assertEqual(deserialized_value.id, field_value["id"]) def test_license_subhandler_deserialize_with_invalid_data(self): """ diff --git a/geonode/metadata/tests/tests.py b/geonode/metadata/tests/tests.py index 42d3fcd43e4..4f82dd55bf1 100644 --- a/geonode/metadata/tests/tests.py +++ b/geonode/metadata/tests/tests.py @@ -94,8 +94,8 @@ def setUp(self): # Setup the database TopicCategory.objects.create(identifier="cat1", gn_description="fake category 1") TopicCategory.objects.create(identifier="cat2", gn_description="fake category 2") - License.objects.create(identifier="license1", name="fake license 1") - License.objects.create(identifier="license2", name="fake license 2") + self.license1 = License.objects.create(identifier="license1", name="fake license 1") + self.license2 = License.objects.create(identifier="license2", name="fake license 2") Region.objects.create(code="fake_code_1", name="fake name 1") Region.objects.create(code="fake_code_2", name="fake name 2") HierarchicalKeyword.objects.create(name="fake_keyword_1", slug="fake keyword 1") @@ -355,8 +355,8 @@ def test_license_autocomplete_no_query(self): self.assertEqual(response.status_code, 200) results = response.json()["results"] self.assertEqual(len(results), 2) - self.assertIn({"id": "license1", "label": _("fake license 1")}, results) - self.assertIn({"id": "license2", "label": _("fake license 2")}, results) + self.assertIn({"id": self.license1.id, "label": _("fake license 1")}, results) + self.assertIn({"id": self.license2.id, "label": _("fake license 2")}, results) def test_license_autocomplete_with_query(self): """ @@ -369,8 +369,8 @@ def test_license_autocomplete_with_query(self): self.assertEqual(response.status_code, 200) results = response.json()["results"] self.assertEqual(len(results), 2) - self.assertIn({"id": "license1", "label": _("fake license 1")}, results) - self.assertIn({"id": "license2", "label": _("fake license 2")}, results) + self.assertIn({"id": self.license1.id, "label": _("fake license 1")}, results) + self.assertIn({"id": self.license2.id, "label": _("fake license 2")}, results) def test_license_autocomplete_with_query_one_match(self): """ @@ -383,7 +383,7 @@ def test_license_autocomplete_with_query_one_match(self): self.assertEqual(response.status_code, 200) results = response.json()["results"] self.assertEqual(len(results), 1) - self.assertIn({"id": "license2", "label": _("fake license 2")}, results) + self.assertIn({"id": self.license2.id, "label": _("fake license 2")}, results) def test_license_autocomplete_with_query_no_match(self): """