-
Notifications
You must be signed in to change notification settings - Fork 301
Support custom model object keys #155
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
So your Would it be possible for you to change your model's I'm a little hesitant to change |
I agree it sounds rocky, but in the context of the app it makes sense. We have an app with a django user model that is backed by LDAP, so the username field is the 'true' primary key and the integer id is an implementation detail for that application. The Emberjs app pulls in data from this system and two others, so exposing the primary key seems risky as its just the ID for this particular row, the username is much safer to share between the systems. Also we view exposing the user id as a security risk, it opens the system up to easy enumeration attacks whereas the username is not susceptible without a large brute forcing effort. Upgrading the central app would be a large undertaking, and we've been using this method for a while without any issues with the DRF ember package. Upgrading the app to use the username as the primary key would be awesome, but it's a fairly critical application and that would be a large/costly undertaking with already stretched resources. Surely it's not too much to just use the |
How would you deal with the relations then? What is the ID used in other objects ForeignKeys? |
There are two or three models being served from the same application, for those foreign keys we just use a CharField with Honestly, its also a bit about enumeration. With ~500 employees (past and present) in the database exposing the integer userID makes it super super easy to gather a big list of everyone who has ever worked for us. From this perspective exposing the username is a much much better approach. |
Upgrading a huge, critical LOB application to use GUIDs (which is only more secure by virtue of being a larger keyspace) is a huge undertaking, whereas this is a genuine use case and what I think is a somewhat simple fix. There are many cases where you may not want to expose the internal ID of an object but instead use another But it's your library and your choice. I'm more than willing to make a MR with the changes and have another discussion based on that? |
@orf we are always open to PRs and I did not want to offend you in any ways. it's just that with my knowledge of the internals of DRF, using another field as the ID for a relation, just seems to be a lot of work and one would need to rewrite a lot of things for a very specific edge-case. Again feel free to open a PR if you come up with a solution. |
I think I'm misunderstanding something about your use case. Isn't supporting alternative primary keys what the
Now If your username field is unique anyway, what's the barrier to simply setting Then third-party code like |
Oh, I realize that the 'username' field is on a different model. Assuming the username field is unique on the user model, and if no two Employees can have a foreign key to the same user (i.e. the foreign key to user on Employee is unique), you could have the Employee.user foreign key reference user.username (instead of user.id) and then set that foreign key field as Employee's primary key. So Employee.user would look like: user = models.ForeignKey(to_field='username', primary_key=True) And if This would involve one schema migration (to adjust the foreign key constraints) and no data migrations. |
Hey, yeah in our case the app is still using the old Django profile system rather than a custom user model, but that's not entirely relevant to the issue I'm reporting. I don't think I explained this properly above, sorry! DRF allows you to expose any attribute you feel fit as the 'id' in a serializer. By default it uses the 'pk' attribute of the model, but this can be changed (see below). All I would like is for this functionality to be exposed by this library, currently it's hard-coded to the class ThingSerializer(ModelSerializer):
id = serializers.CharField(source='attribute')
class Meta:
fields = ('id',)
model = Thing
class RelatedThing(ModelSerializer):
# produces a list of the charfield attributes defined above, rather than integer PK's
some_relation = ThingSerializer(many=True)
... My particular use case is a bit convoluted, but the core of it is that this library naively assumes that you only want to expose the |
TODO Test coverage There are so many places where PK is assumed in djangorestframework-jsonapi and so many places/ways to change IDs/URLs in DRF itself, that getting coverage on all the edge cases will be quite a lot of work. Some of that work may be better spent re-factoring things to make the code easier to understand and reduce the number of edge cases in the first place.
If anyone's interested, I've added support for defering to |
Thank you! |
Hey @rpatterson are you able to make a MR with that change? |
Hi everyone, What is the status of this? Is it going to be merged eventually? It would be great to be able to use custom fields for API lookups and model PKs for model relationships. |
This merge request is unlikely to be merged as I have received no feedback
from the maintainers regarding this, and there are conflicts.
To reiterate, there is absolutely no technical reason why this cannot be
supported. The underlying issue is that this library, unlike django rest
framework, hard codes the primary key field in a few places and incorrectly
assumes you always want to use that. Rest framework does not hard code this
and correctly uses the model serialiser to identify the primary key field.
…On 12 May 2017 17:55, "Luca Corti" ***@***.***> wrote:
Hi everyone,
What is the status of this? Is it going to be merged eventually? It would
be great to be able to use custom fields for API lookups and model PKs for
model relationships.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#155 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AA-sh0T6dTYb3F_sNKgsEZl0Xi1F5STUks5r5I77gaJpZM4Glvcc>
.
|
@orf I share your views on the matter. This is probably the result of too much coupling between models and serializers. Direct mapping of resources to database models is a convenient but limited approach IMHO. The ability to use serializers to do this kind of mapping is really important. |
I agree, that is exactly why serialisers exist and is literally their
function. To bypass that for seemingly no good reason is really annoying,
especially if you want/need to use a json API and this is blocking you.
Receiving no feedback on your merge request and incorrect comments on the
related issues from maintainers is even more annoying.
…On 12 May 2017 19:36, "Luca Corti" ***@***.***> wrote:
@orf <https://github.com/orf> I share your views on the matter. This is
probably the result of too much coupling between models and serializers.
Direct mapping of resources to database models is a convenient but limited
approach IMHO. The ability to use serializers to do this kind of mapping is
really important.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#155 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AA-sh6gSsIyykhmp_SWmb3v9BVGU_BGEks5r5KbKgaJpZM4Glvcc>
.
|
@orf please remember that maintainers are fallible human beings who volunteer their time to work on this project. You appear to be frustrated and the best I can do is apologize. I hope you don't take the lack of feedback personally. I believe that all the original maintainers have moved to a mostly inactive role for many months so all issues and PRs have seen a low volume of feedback. I picked up the maintainer torch at the end of February and have slowly been working through PR queue to get things going again. I have extremely limited time for DJA and will prioritize on PR work and work through issues when I can. As for this actual issue, I'm happy to consider a PR if DJA is blocking something that is possible with vanilla DRF. That sounds like a bug to me as well. If a previous PR was closed because of conflicts, it's only because I don't have the knowledge to resolve the conflicts or time to fix the conflicts myself. Wearing the new maintainer hat, I'm really looking to open up DJA to all contributors/contributions. That's not to say that the other maintainers didn't, but my focus is on a permissive model where I indirectly help by shaping incoming work from other contributors. I'm most interested in the following goals:
I hope that motivates you enough to put your work back up for consideration in a new PR. I'd love to talk through some code. 😄 |
@mblayman I'm sorry if I am causing a dispute over this while trying to make a technical point. Actually, I think you are right about this being a bug in DRF-JA since DRF has a different behaviour as pointed out by @orf I'm interested in getting this in as I'm currently working on something which needs this capability, so I'm willing to spend some time on getting a PR together if you would consider merging this. AFAIU the most updated version of the PR is currently on @rpatterson fork. Any help in getting up to speed on the original PR would be great. |
I am overjoyed to keep this discussion technical. 😄 My last comment was to provide an update about what's going on with the maintainers so everyone has context of the project's status. I'm happy to get back on track with the problem reported in this issue. |
Thanks for the above suggestions, but how then multiple see http://discuss.jsonapi.org/t/composite-id-inside-the-resource-object/367/3 |
Being able to select which field to serialize for the primary key would be very useful. The application I'm working on must maintain the original primary keys for backwards compatibility with the old API while we transition to using UUIDs for the new API. We do not want to expose the original integer primary keys at all. I almost got this to work by extending class CustomJSONRenderer(rest_framework_json_api.renderers.JSONRenderer):
@classmethod
def extract_attributes(cls, fields, resource):
obj = super(CustomJSONRenderer, cls).extract_attributes(fields, resource)
obj.pop('uuid')
return obj
@classmethod
def build_json_resource_obj(cls, fields, resource, resource_instance, *args, **kwargs):
obj = super(CustomJSONRenderer, cls).build_json_resource_obj(fields, resource, resource_instance, *args, **kwargs)
obj['id'] = str(resource_instance.uuid)
return obj |
This would also be useful for model-less serializers. |
This issue only concerns For a use case where there is no model you can simply use a |
I experienced this issue using the plain class Foo:
def __init__(self):
# pk instead of id required here if not it barfs
self.pk = '1'
class FooSerializer(serializers.Serializer):
id = serializers.CharField(read_only=True)
serializer = FooSerializer(instance=Foo()) Am i doing something wrong? |
@mynameistechno Here are some serializers which are working. They use a custom queryset but would be the same as when using a python plain object with a pk set. |
I still experience this issue. "object has no attribute pk' Exception on this line here:
My object is not an instance of a model. I have an id attribute set on the object however. I am using vanilla Serializer, not ModelSerializer. The only way (that I know) to get around this is to set a pk attribute instead of an id on your instance object. As far as I can tell the renderer (build_json_resource_obj) assumes a pk is present on the object. |
@mynameistechno To be able to configure the attribute/property to be used as id resp. primary key a solution for this issue would need to be implemented. |
Ok I'm stuck on this issue. My data comes from a third-party SDK which just returns a list of dictionaries. It's complex data with nested lists and such, but in the end it's just a list of dicts. I'm posting my snippets here changing the name to not expose the 3rd party provider. Here is the serializer I have so far:
This is my view:
sdk.get_all_the_things() returns a list of dicts, similar to:
I have no control over this input directly and the underlying dict structure could change at any time. Again the format of the dict isn't all that critical. This just gives me the dreaded |
@daveseff setattr(obj_dict, 'pk', 'value') |
Yeah I ended up addressing this in a similar fashion. I take the underlying data dict and create an object instance from a simple class definition. The class has a read only pk property method that returns the id. |
Thank you kindly @sliverc and @mynameistechno . I managed to get this working by creating a dummy object and populating a list with those objects. I'm posting my solution here in case any one else comes across this and needs a solution:
Then had to manipulate the list of dicts. This is somewhat ugly code but worked:
Then Changed my serializer to serialize the object data:
|
If the |
Hey, thanks for your work on this project. I was investigating integrating it with our EmberJS app but I ran into an issue. We have an
Employee
model that has a normal integer primary key, but also ausername
attribute. I'd like to only expose theusername
as the primary key, using DRF this is simple:However in
build_json_resource_obj
it just uses instance.pk:Can you use the serializers ID field if available, and fallback to the instances PK?
The text was updated successfully, but these errors were encountered: