Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/221.canada.changes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added a unique constraint `con_package_resource_unique_position` onto the Resource model to make resource positions unique per dataset. This is a deferrable constraint. Resource with a deleted state will now have `null` positions. Requires DB plugin migration.
3 changes: 3 additions & 0 deletions ckan/lib/dictization/model_save.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ def package_resource_list_save(
# Mark any left-over resources as deleted
for resource in set(old_list) - set(obj_list):
resource.state = 'deleted'
# (canada fork only): unique resource position constraint
# TODO: upstream contrib!! w/ migration script
resource.position = None
resource_list.append(resource)

return True
Expand Down
34 changes: 31 additions & 3 deletions ckan/model/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@
from __future__ import annotations

import datetime
from typing import Any, Callable, ClassVar, Optional
# (canada fork only): unique resource position constraint
# TODO: upstream contrib!! w/ migration script
from typing import Any, Callable, ClassVar, Optional, List


from collections import OrderedDict
from sqlalchemy.ext.orderinglist import ordering_list
from sqlalchemy import orm
from ckan.common import config
from sqlalchemy import types, Column, Table, ForeignKey
# (canada fork only): unique resource position constraint
# TODO: upstream contrib!! w/ migration script
from sqlalchemy import types, Column, Table, ForeignKey, UniqueConstraint
from typing_extensions import Self

import ckan.model.meta as meta
Expand Down Expand Up @@ -55,6 +59,11 @@
Column('url_type', types.UnicodeText),
Column('extras', _types.JsonDictType),
Column('state', types.UnicodeText, default=core.State.ACTIVE),
# (canada fork only): unique resource position constraint
# TODO: upstream contrib!! w/ migration script
UniqueConstraint(Column('package_id'), Column('position'),
name='con_package_resource_unique_position',
deferrable=True, initially='deferred')
)


Expand Down Expand Up @@ -164,13 +173,32 @@ def related_packages(self) -> list[Package]:

## Mappers

# (canada fork only): unique resource position constraint
# TODO: upstream contrib!! w/ migration script
def _get_stately_resource_positions(index: int, resources: List[Resource]):
"""
Give state='deleted' resources null positions.

Required for con_package_resource_unique_position
unique constraint when deleting resources.
"""
if resources[index].state != 'deleted':
return index
return None


meta.mapper(Resource, resource_table, properties={
'package': orm.relation(
Package,
# all resources including deleted
# formally package_resources_all
backref=orm.backref('resources_all',
collection_class=ordering_list('position'),
collection_class=ordering_list(
'position',
# (canada fork only): unique resource position constraint
# TODO: upstream contrib!! w/ migration script
ordering_func=_get_stately_resource_positions
),
# (canada fork only): proper resource position order for package ORM object
# TODO: upstream contrib!!!
order_by=resource_table.c.position,
Expand Down