|
| 1 | +from datetime import datetime |
| 2 | + |
1 | 3 | from udata.core.access_type.constants import ( |
2 | 4 | AccessAudienceCondition, |
3 | 5 | AccessAudienceType, |
@@ -213,13 +215,68 @@ class DatasetForm(ModelForm): |
213 | 215 | _("Private"), |
214 | 216 | description=_("Restrict the dataset visibility to you or your organization only."), |
215 | 217 | ) |
| 218 | + published_at = fields.DateTimeField(_("Publication date")) |
216 | 219 |
|
217 | 220 | owner = fields.CurrentUserField() |
218 | 221 | organization = fields.PublishAsField(_("Publish as")) |
219 | 222 | extras = fields.ExtrasField() |
220 | 223 | resources = fields.NestedModelList(ResourceForm) |
221 | 224 | contact_points = fields.ContactPointListField(validators=[validate_contact_point]) |
222 | 225 |
|
| 226 | + # ================================================================================== |
| 227 | + # Backward compatibility layer for `private` field |
| 228 | + # ================================================================================== |
| 229 | + # The `private` boolean field has been replaced by `published_at` (datetime) on the |
| 230 | + # Dataset model. However, we need to maintain API backward compatibility. |
| 231 | + # |
| 232 | + # Challenges encountered: |
| 233 | + # 1. MongoEngine rejects unknown fields in constructor - we can't just add a |
| 234 | + # `private` property with a setter on the model because ModelForm.save() passes |
| 235 | + # self.data directly to the model constructor, which fails with FieldDoesNotExist. |
| 236 | + # |
| 237 | + # 2. WTForms BooleanField.process_formdata() doesn't update self.data when the value |
| 238 | + # is empty/None, so we can't rely on self.private.data to detect explicit None. |
| 239 | + # |
| 240 | + # 3. self.data is a read-only property computed from field values, so we can't |
| 241 | + # modify it directly (e.g., self.data.pop("private") fails). |
| 242 | + # |
| 243 | + # Solution: |
| 244 | + # - Override __init__ to populate private.data from the existing instance |
| 245 | + # - Override populate_obj to skip the private field (model has no such field) |
| 246 | + # - Override save to: |
| 247 | + # a) Check self.formdata (raw JSON dict) to detect if private was explicitly sent |
| 248 | + # b) Convert private -> published_at before creating/updating the instance |
| 249 | + # c) Exclude private from the data dict when creating new instances |
| 250 | + # ================================================================================== |
| 251 | + |
| 252 | + def __init__(self, *args, **kwargs): |
| 253 | + super().__init__(*args, **kwargs) |
| 254 | + if self.instance: |
| 255 | + self.private.data = self.instance.published_at is None |
| 256 | + |
| 257 | + def populate_obj(self, obj): |
| 258 | + for name, field in self._fields.items(): |
| 259 | + if name != "private": |
| 260 | + field.populate_obj(obj, name) |
| 261 | + |
| 262 | + def save(self, commit=True, **kwargs): |
| 263 | + if self.formdata and "private" in self.formdata: |
| 264 | + private_value = self.formdata.get("private") |
| 265 | + if private_value is True: |
| 266 | + self.published_at.data = None |
| 267 | + elif private_value is False or private_value is None: |
| 268 | + self.published_at.data = datetime.utcnow() |
| 269 | + |
| 270 | + if self.instance: |
| 271 | + self.populate_obj(self.instance) |
| 272 | + else: |
| 273 | + data = {k: v for k, v in self.data.items() if k != "private"} |
| 274 | + self.instance = self.model_class(**data) |
| 275 | + |
| 276 | + if commit: |
| 277 | + self.instance.save(**kwargs) |
| 278 | + return self.instance |
| 279 | + |
223 | 280 |
|
224 | 281 | class ResourcesListForm(ModelForm): |
225 | 282 | model_class = Dataset |
|
0 commit comments