Improve support for non-integer primary keys#279
Conversation
|
Note that filtering on foreign key is now handled by this code: # For foreign keys and similar fields, we should use the filter of the primary key of the related object
class_for_filtering = field.__class__
if isinstance(field, models.ForeignObject) or isinstance(field, models.ManyToManyField) or isinstance(field, models.ForeignObjectRel):
class_for_filtering = field.related_model._meta.pk.__class__Prior to this PR, it was hardcoded as part of |
de3d364 to
ce5801c
Compare
BBooijLiewes
left a comment
There was a problem hiding this comment.
Lets also test this in a few internal project pipelines before merging.
| before = model_class.format_field_for_history(field_name=c.field, raw_value=c.before, is_before=True, diff_tracker=diff_tracker, oid=oid) | ||
| changes.append({'model': c.model, 'oid': c.oid, 'field': c.field, 'diff': c.diff, 'before': before, 'after': after}) | ||
| data.append({'date': cs.date, 'uuid': cs.uuid, 'id': cs.id, 'source': cs.source, 'user': cs.user_id, 'changes': changes}) | ||
| data.append({'date': cs.date, 'uuid': cs.uuid, 'id': cs.pk, 'source': cs.source, 'user': cs.user_id, 'changes': changes}) |
There was a problem hiding this comment.
Check serialization compisite PK, maybe add test case?
There was a problem hiding this comment.
I just added a fix + test case for string (non-integer) primary keys.
I also tried to add a test case for composite primary keys, but this is currently impossible:
- Binder currently doesn't support detail endpoints for models with composite pk's, so I can't use
/api/model_with_composite_pk/pk_tuple/history/. - Django doesn't support forward relations to models with composite pk's
- Our history endpoint ignores backward relations
|
|
||
|
|
||
| class BinderModel(models.Model, metaclass=BinderModelBase): | ||
| pk_regex = '[0-9]+' |
There was a problem hiding this comment.
Now that PK's can be composite does this still hold? Or should we add check in places?
There was a problem hiding this comment.
Currently, this PR does not support detail endpoints for models with composite primary keys. For instance, if I create an OrderAddition model with a composite primary key (order_id, addition_number), there is currently no way to address it. What would it even look like? /api/order_addition/(1234,5)/?
Also note that this pk_regex = '[0-9]+' is the default regex, which models with more complicated primary keys can override.
| if isinstance(entry, list) and len(entry) == 1: | ||
| entry = entry[0] |
There was a problem hiding this comment.
Add comment what this does
| else: | ||
| return models.CompositePrimaryKey('contact_person', 'date') |
There was a problem hiding this comment.
Can we also build a check in binder itself if you try to use a composite key on a binder model with binder < 5 we give a warning on migration?
There was a problem hiding this comment.
I assume you mean django < 5 rather than binder < 5.
Django < 5 simply doesn't support CompositePrimaryKey, so it will already give an error.
If you use a CompositePrimaryKey on django 5 but with an old binder version, django won't give any errors, but binder will fail miserably. I could add a helpful error message, but older versions of binder wouldn't have that helpful error message, which makes it useless.
| # I would like to remove the next line completely, but that might break downstream projects | ||
| data['id'] = data[self.model._meta.pk.name] |
There was a problem hiding this comment.
No comment in first person
| queryset = queryset.annotate(**{name: annotations[name] for name in required_annotations}) | ||
| try: | ||
| obj = queryset.get(pk=int(after_id)) | ||
| obj = queryset.get(pk=int(after_id)) # TODO Check this? |
|
|
||
|
|
||
| def _is_classic_id(self, pk): | ||
| return (pk.name == 'id' and isinstance(pk, models.IntegerField)) or (pk.name.endswith('_ptr') and isinstance(pk, models.ForeignObject)) |
There was a problem hiding this comment.
think modern djanbo uses a big int field by default.
There was a problem hiding this comment.
Possibly, but note that BigIntegerField extends IntegerField: https://github.com/django/django/blob/4bbc27c8686f10f9556cef02dbfa9f5157fbcf56/django/db/models/fields/__init__.py#L2241
| continue | ||
|
|
||
| if isinstance(data[field.name], int): | ||
| if isinstance(data[field.name], int): # TODO Check right type? |
| elif isinstance(data[field.name], list): | ||
| for i, mid in enumerate(data[field.name]): | ||
| if isinstance(mid, int): | ||
| if isinstance(mid, int): # TODO Check right type? |
| raise BinderRequestError('with.{}.{} should be a list'.format(model.__name__, field.name)) | ||
| for i, v in enumerate(values[field.name]): | ||
| if not isinstance(multiput_get_id(v), int): | ||
| if not isinstance(multiput_get_id(v), int): # TODO Check right type |
ac444e6 to
c639c89
Compare
This commit dramatically improves the way binder handles non-integer primary keys. Currently, binder sort of allows it, but: - You cannot invoke detail endpoints - You cannot invoke detail routes - You cannot invoke the history route - You cannot create, modify, or delete such objects - The name of the primary key field is always sent as `id` over the network, regardless of what the name of the primary key field is. - Any relations will fail miserably because binder accepts only integer IDs. This commit fixes all of these shortcomings.
Before updating django-hijack, the /user/endmasquerade/ endpoint would yield 200 when it succeeds, and yield 403 when it fails because the user is not masquerated. After updating django-hijack, it would always yield 200. This commit restores the old behavior of returning 403 for non-masquerated users.
This commit improves our support for `models.CompositePrimaryKey` by properly handling models with `CompositePrimaryKey`s in the `with`s of other objects. (Prior to this commit, it would fail miserably.)
This PR improves support for non-integer primary keys
Currently, binder sort of allows it, but:
idover the network,regardless of what the name of the primary key field is.
This PR fixes all of these shortcomings.
Furthermore, it fixes some history changeset inconsistencies.