I am using a nested serializer. I need ProfileSerializer to return full related Project object for get requests and consider only id switching (changing current) like with relatedPrimaryField behaiviour for post/put requests on ProfileSerializer. any solutions on how to achieve this ?
class ProfileSerializer(serializers.ModelSerializer):
current = ProjectSerializer()
class Meta:
model = Profile
fields = ('function', 'current')
As Linova mentioned, the easiest way to solve this issue without using a third-party library is to declare two separate fields in your serializer. Your nested serializer current would stay the same, but you would add a new PrimaryKeyRelatedField serializer. The nested serializer should be read only, but the related field would not be read only. I normally name the related field <field>_id by convention.
In GET requests, both the nested serializer and the id field will be returned, but for PUT or POST requests only the <field>_id needs to be specified.
class ProfileSerializer(serializers.ModelSerializer):
current = ProjectSerializer(read_only=True)
current_id = serializers.PrimaryKeyRelatedField(queryset=Projects.objects.all(), source='current')
class Meta:
model = Profile
fields = ('function', 'current', 'current_id')
The most consistent way I usually advice is to mark all the nested serializer (ProjectSerializer in this case) as read_only and add the id field as read_only=False
You'll therefore have consistence between the list/retrieve and creation/updates.
Related
Using latest version of Django and DRF.
I have a rather complex requirement I can't find a solution for. I'll try to simplify it.
Let's say I have a model that has two fields. field_a and field_b
I have a ModelSerializer for it. I POST a request with its fields. The fields get validated with the model and then against my two serializer functions validate_field_a and validate_field_b. All is well.
Now I'd like my POST request to include a third field that is not a member of that model. let's call it field_c. I have a custom def create(self, validated_data): in my serializer which saves everything to the database.
with regards to field_c I would like to:
Custom Validate it. just like I do with the other two fields.
Require that it is mandatory for the whole request to succeed and if it's not, issue a "Field is required" error just like if I forgot to POST one of my required model fields.
Have the chance to take field_c and save it onto a totally different unrelated Model's row in the db.
I can't seem to get around that. If I add field_c to the fields meta - it throws an exception saying justifiably that field_c is not in my model. If I don't include it in fields, the validate_field_c which I really want to put there doesn't even get called.
What can I do?
You can add the custom field in your serializer as a write_only field and override the create method so that you can handle the custom field's value.
Something like this:
class MySerializer(serializers.ModelSerializer):
field_c = serializers.CharField(write_only=True)
class Meta:
model = MyModel
fields = ('field_a', 'field_b', 'field_c')
def validate_field_c(self, value):
if value is 'test':
raise ValidationError('Invalid')
return value
def create(self, validated_data, **kwargs):
field_c = validated_data.pop('field_c')
return MyModel.objects.create(**validated_data)
Don't use ModelSerializer for this - use a serializer that recreates the same fields as your model & include field_c as you would.
I understand that you want your model to do some of the work in the validation process but the design of DRF is such that it isolates these responsibilities. You can read more about it here. Basically, the serializer should be the one doing all the validation heavy-lifting.
Of course, this means that you'll have to explicitly define the validation methods in the serializer.
In your custom create() method you can create the model instance or do whatever you want in it as required.
I have a database schema that has each object of a certain type being stored across two separate tables (one row in each table, different data in each, with a foreign key from one to the other.)
Unfortunately, Django Rest Framework tends to assume that there is a one to one correspondence between serializers and models, which is not true of my case. How should I be approaching this? It seems like the serializer should return the representation of the object which will be the actual HTTP response of the ajax requests, so using two serializers doesn't seem right. I've looked at extending BaseSerializer (which is how I currently plan to implement this if I don't find better solutions), but certain methods take in an instance, which should contain all the data needed to serialize the object, whereas I have two instances relevant.
Any advice would be super appreciated! Thank you.
Writable nested representations section might help you.
You have 2 models ModelA and ModelB. Create your first model's serializer
class ModelASerializer(serializers.ModelSerializer):
class Meta:
model = ModelA
fields = ('fields',..) #
Then in other model's serializer add the first serializer and override the required methods (like create, update). Something like this:
class ModelBSerializer(serializers.ModelSerializer):
# add the serializer for the foreignkey model
model_a = ModelASerializer()
class Meta:
model = ModelB
fields = ('fields',..) #
def create(self, validated_data):
modela_data = validated_data.pop('model_a')
model_b = ModelB.objects.create(**validated_data)
ModelA.objects.create(model_b=model_b, **modela_data)
return model_b
# override update too ..
I'm feeding serialized data from the Django rest framework to a Javascript pivot table on my site. If I have a variable called 'created_on', the DRF uses that as the field name. What I want to display in my pivot table is the label which will be converted to 'Created On'.
As an example, my output from DRF is the following:
[{"created_on": "2016-04-23"}, {"created_on": "2016-05-23"}]
What I want is:
[{"Created on": "2016-04-23"}, {"Created on": "2016-05-23"}]
Is this possible without me overriding the serialization process?
No, its not possible (currently) without overriding the serialization process.
Why it is not possible?
This is because the alternate name which you want to use for created_on contains whitespace and its not possible to define a field in your serializer having whitespaces in it. Also, there is no functionality currently to provide alternate name for a field to be used in serialization process.
Possible Solution:
You can override the to_representation() method of your serializer and there add a Created On key having value equal to the value of created_on key. Then all the serialized objects will contain a key Created On.
class MySerializer(serializers.ModelSerializer):
...
def to_representation(self, obj):
primitive_repr = super(MySerializer, self).to_representation(obj)
primitive_repr['Created On'] = primitive_repr['created_on']
return primitive_repr
What if the alternate name did not contain any whitespaces?
Had the alternate name did not contain any spaces between them, you could then have used SerializerMethodField() with source argument.
You could have done something like:
class MySerializer(serializers.ModelSerializer):
alternate_name = serializers.SerializerMethodField(source='created_on')
class Meta:
model = MyModel
fields = (.., 'alternate_name')
In Django Rest Framework, what's the appropriate way to write a hyperlinked serializer for a model that has a property that points to a reverse related object or None?
class AModel(models.Model):
a_name = models.CharField()
#property
def current_another_model(self):
# Just an example, could be whatever code that returns an instance
# of ``AnotherModel`` that is related to this instance of ``AModel``
try:
return self.another_model_set.objects.get(blah=7)
except AnotherModel.DoesNotExist:
return
class AnotherModel(models.Model):
blah = models.IntegerField()
our_model = models.ForeignKey(AModel)
How do we write a serializer for AModel that contains the url (or null, of course) for the property current_another_model?
I've tried this (I have a working AnotherModel serializer and view):
class AModelSerializer(serializers.HyperlinkedModelSerializer):
current_another_model = serializers.HyperlinkedRelatedField(read_only=True, view_name='another-model-detail', allow_null=True)
That gets me this error:
django.core.exceptions.ImproperlyConfigured: Could not resolve URL for hyperlinked relationship using view name "another-model-detail". You may have failed to include the related model in your API, or incorrectly configured the `lookup_field` attribute on this field.
Define AnotherModel serializer and define that dependency within AModelSerializer, also add class Meta in your AModelSerializer.
class AModelSerializer(serializers.ModelSerializer):
current_another_model = AnotherModelSerializer(allow_null=True, required=False)
class Meta:
model = AModel
fields = ('current_another_model',)
That code should do the trick for you.
Another way is stick to the official documentation while defining related serializers.
Update: Found quite similar question Django Rest Framework - Could not resolve URL for hyperlinked relationship using view name "user-detail"
which is solved.
I have a django 1.6 app with the following (trimmed for clarity)
classes defined. User is the standard django.contrib.auth User class.
class Event(models.Model):
user = models.ForeignKey(User, related_name='events')
name = models.CharField(max_length=64)
class Profile(models.Model):
user = models.ForeignKey(User, related_name='aprofile')
class MemberProfile(Profile):
pass
Here are my admin classes:
class ProfileAdmin(ModelAdmin):
model = Profile
fields = ('user', )
class MemberProfileAdmin(ModelAdmin):
model = MemberProfile
fields = ('user', )
readonly_fields = ('user', )
What I'd like to do is display a read-only list of all events for a given member, or at least profile. Of course joining across the User foreign key seems like the way to go, but I am drawing a blank as to how to accomplish this. Here's a summary of attempts so far.
Define an inline admin on the Event class directly referencing the user field, and add it to the ProfileAdmin:
class EventInlineAdmin(TabularInline):
model = Event
fk_name = 'user' # Fails - fk_name 'user' is not a ForeignKey to <class 'solo365.solo_profile.models.profile.Profile'>
...well, no, it sure isn't. But our User has an 'aprofile' field, so...
class EventInlineAdmin(TabularInline):
model = Event
fk_name = 'user__aprofile' # Fails - EventInlineAdmin.fk_name' refers to field 'user__aprofile' that is missing from model 'admin_fk_test.Event'.
Ok, those fields look like they should sync up, but perhaps we need to be a little more aggressive:
class EventInlineAdmin(TabularInline):
model = Event
fk_name = 'user__aprofile__pk' # Fails - 'EventInlineAdmin.fk_name' refers to field 'user__aprofile__pk' that is missing from model 'admin_fk_test.Event'.
I've also tried messing with formfield_for_foreignkey() and friends in both the inline and the regular model admins, but without that fk_name having a valid value, those methods never get called.
I then considered trying to access the events field directly from a Profile's user:
class ProfileAdmin(ModelAdmin):
model = Profile
fields = ('user', 'user__events') # Fails - Unknown field(s) (user__events) specified for Profile. Check fields/fieldsets/exclude attributes of class ProfileAdmin.
What about with a custom formfield_for_foreignkey() method? Sadly that never gets called for anything other than the 'user' field. I've also considered a custom get_formsets() method, but frankly I'm not sure how I could use that without a working EventInlineAdmin.
I could of course define a custom field that simply concatenates all of the events and returns that as a string, but ideally I would prefer something like a fully-featured inline (even read-only) than just a chunk o' text. IOW such a custom field would have a method that (ideally) would return an inline form without requiring any sort of custom template, setting of allow_tags, etc.
Am I doomed to have to create a completely custom Form for the Profile admin class? Or is there a simple way to accomplish what I'm trying to do, that I'm just missing?
Update:
Bonus points if a provided solution works for the MemberProfileAdmin class, not just the ProfileAdmin class.
The relation between User and Profile should be a 1:1 relation which would allow the referencing via user__aprofile. Otherwise, the reverse relation of a foreing key is a queryset because one foreign key can be assigned to multiple instances. This is might be the reason why your code failed.
Change it to:
class Profile(models.Model):
user = models.OneToOneKey(User, related_name='aprofile')
This is a bit like using ForeignKey(unique=True).
To know the attributes, it might help to call dir(model_instance) on the model instance in question, or try around in the Django shell (./manage.py shell).
Also, I've experienced that it might be more confusing to assign a custom related_name like in your case where you would expect one profile by looking at the related name but you would actually get back a queryset.
The generated name in that case would be profile_set, and you would have to call profile_set.all() or profile_set.values() to get some actual profiles.