How do you make a Django Rest Framework serializer bi-directional - django

I have an angular app that presents a form with contact data and a list of contact numbers. Each contact can have N numbers and each of those numbers has a type (cell,home,work...) The code below will send the json to the angular app just fine and I can deal with it there including adding new numbers, removing numbers ..... However when DRF gets the exact same format json back, it can't deserialize it. It throws off this error:
AttributeError: 'Contact' object has no attribute 'numbers'
which is totally valid, but the serializer DOES have that field and should be able to hang onto those values so I can save them after I save the contact.
If I do something totally hokey like this in the update method:
self.object = self.get_object_or_none()
self.object.numbers = []
I can eliminate that error, but then it throws off these kind of errors:
{'numbers': [
{u'non_field_errors': [u'Cannot create a new item, only existing items may be updated.']},
{u'non_field_errors': [u'Cannot create a new item, only existing items may be updated.']},
{u'non_field_errors': [u'Cannot create a new item, only existing items may be updated.']}
]}
The first two phone numbers aren't new, they have id fields and came from the db, the third one is new, I'm trying to add it.
Here is the code. Surely this isn't that bizarre a way to do things. Is Django Rest Framework what I should be using? I keep running into show stoppers like this that seem to be the documented way to do things, but then they blow up spectacularly.
class PhoneTypeSerializer(serializers.ModelSerializer):
class Meta:
model = PhoneType
class ContactPhoneSerializer(serializers.ModelSerializer):
number_type = PhoneTypeSerializer(source='number_type')
class Meta:
model = ContactPhone
depth = 1
exclude = ('owner',)
class ContactSerializer(serializers.ModelSerializer):
numbers = ContactPhoneSerializer(source='number_set', many=True, required=False)
class Meta:
model = Contact
How do I deserialize this data so I can save it?

I had the same issue and solved it by adding to the serializer some flags. In your case it should be something like:
number_type = PhoneTypeSerializer(source='number_type', many=True, required=False, allow_add_remove=True, read_only=False)
A bit late, but maybe it still helps. Found this suggestion here:
Updating m2m not possible when using serializers as fields

Related

Retrieving multiple rows from seperate model in django relationships

I've been struggling with this puzzled for a few hours. Here's a schema of what I'm trying to do.
I have a user model and a profile model, it's a one-to-one relationship, but I'd like to be able to query a user and retrieve all the email addresses (from the User model) for users that share the same company (from the Profile Model). To be fair, my understanding of django is limited, but I went through the serializer relations guide and tried my hands at most approach described there, to no avail. At this point, I'm not even sure I'm on the right path.
So, my understanding of it is
From the user, I need to fetch the profile (a source='profile' approach may work)
From that profile, I need to retrieve the company
From that company, I need to retrieve all the user_id that belongs to that company
From those user_ids, I need to lookup the email fields of all those users
I need to also filter out the email address of the user making the request
Does this make any sense? At this point, I'm trying to accomplish all of that from the serializer, but was unsuccessful. Here are some snippets of code I tried, but I doubt any of them will point towards any form of solution, unfortunately.
class TeamEmailsSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['email']
class UserSerializer(serializers.ModelSerializer):
...
# only one of them was present at a time, but none gave any promising results
test_one = Profile.objects.filter(source=profile.company.id).values_list('user_id', flat=True)
test_one = serializers.RelatedField(source='profile.company.id', read_only=True)
test_one = TeamEmailsSerializer(many=True, read_only=True)
test_one = serializers.PrimaryKeyRelatedField(source='email', queryset=User.objects.filter())
class Meta:
model = User
fields = (
'test_one'
)
I'm grateful for any clue that may lead towards a solution.
First, you should add company FK on your user as well, it will make things much easier for you.
Then you can define a new method on User model:
class User(AbstractBaseUser):
...
def other_users_emails(self):
return self.company.users.exclude(pk=self.id).values_list('email', flat=True)
Then in your serializer add 'other_users_emails' to the fields list.
Alternatively you could modify to_representation method on your serializer and add 'other_users_emails' attribute directly there

Django Rest Framework - Translating external unique code to a internal id and vice versa

I am building a Rest Api that read and update a model called Requirements.
I am using a ModelSerializer.
The Requirements model has a foreign key on the Materials model.
The problem is that my api user does not know the internal id of Materials
He knows only a code that is unique for the material.
Now, the idea is that in PUT he passes me the material_code and I set the material id and in GET he receives the material code based on the material foreign key of Requirements
I managed to make PUT to work by overriding the validate method and declaring:
material_code = serializers.CharField(max_length=50);
This is the code supplied in the end of the post. Notice, please, that this is a snippet of the complete code that is much complex. In the complete code the Requirements serializer is nested inside another serializer that is nested is nested inside another serializer. But I do not think this is relevant to the problem.
Then I manage to make GET to work by the use of a custom source option in material_code field where the source is a property on my Requirements model. For this the declaration must be changed to:
material_code = serializers.ReadOnlyField(source='get_material_code')
For some reason both:
material_code = serializers.Field()
and
material_code = serializers.Field(source='get_material_code')
behaves in a weird way and do not work either with PUT or with GET, raising an exception “Field.to_internal_value() must be implemented”. I've tried to implement to_internal_value and give it a try but failed. And after all, material_code should not go to a internal value. Once I've managed to set the material id, I do not need it.
There is no way I can make both PUT and GET work simultaneously. GET will only work with serializers.ReadOnlyField and PUT with a serializers.CharField.
Using PUT with serializers.ReadOnlyField generates Exception Type: KeyError Exception Value 'material_code'.
Using GET with serializers.CharField generates Exception Type: AttributeError
Exception Value: Got AttributeError when attempting to get a value for field material_code on serializer RequirementsSerializer. The serializer field might be named incorrectly and not match any attribute or key on the Requirements instance. Original exception text was: 'Requirements' object has no attribute 'material_code'.
Maybe the whole approach is wrong.
What I need is a translation between the externally visible code and the internal id.
This should not be that hard. This is my first Python project and maybe there is a built in way do make it work in Django Rest Api, but I was unable to find in the documentation.
I will deeply appreciate any help. By the way, this is my first stackoverflow post. If I did anything wrong, please let me know.
class Materials (models.Model):
class Meta:
db_table = 'materials'
code = models.CharField(max_length=50);
full_name = models.CharField(max_length=50);
def __str__(self):
return self.full_name
class Requirements (models.Model):
class Meta:
db_table = 'requirements'
material = models.ForeignKey(Materials, on_delete=models.CASCADE)
acceptance_method = models.CharField(max_length=4)
#property
def get_material_code(self):
return self.material.code
class RequirementsSerializer(serializers.ModelSerializer):
material_code = serializers.CharField(max_length=50);
material = serializers.HiddenField(default=1)
class Meta:
model = Requirements
fields = [ 'id',
'material' ,
'material_code' ,
'acceptance_method'
]
read_only_fields = ['material']
def validate(self, data):
# Fill material with a pk on Materials models
# This is necessary since the API receive a material code instead of the model id
if Materials.objects.filter(code = data['material_code']).exists() :
data['material'] = Materials.objects.get(code = data['material_code'])
else:
raise serializers.ValidationError('Material '+ data['material_code'] + ' does not exists')
return data
If I am understanding correctly, you want the user to be able to do a PUT using the material code as the identifier of the material (instead of the ID), and you want the same behavior from the GET call. If this is the case, I believe you are looking for the SlugRelatedField of serializers. Example:
class RequirementsSerializer(serializers.ModelSerializer):
material = serializers.SlugRelatedField(slug_field='code')

Serializing and storing JSON array in Django REST Framework

I'm trying to make a demo website for job applications using Angular 6 and Django Rest Framework. One of my application fields involve listing your interests in a chip input field like this one.
The JSON being sent to my API would look something like this:
{
...
'firstname': 'Firstname',
'lastname': 'Lastname'
...
'interests': ['hobby1', 'hobby2', 'hobby3', 'hobby4'],
...
}
Now as far as I know Django REST Framework supplies a serializer field that is written something like this:
interests = serializers.ListField(
item = serializers.CharField(min_value=xx, max_value=xx)
)
My question is, how do I proceed from here? What model fields do I use, or do I have to create my own save function that iterates through the interests and saves every single one?
Many to many relationship is the thing you are looking for.
You can even have nested serializer so the output of the parent objects would include the serialized interests in the JSON.
class ParentSerializer(serializers.ModelSerializer):
child_set = ChildSerializer(many=True)
class Meta:
depth = 1
model = Parent
fields = ('id', 'other_atributes', 'child_set')
Also you can easily edit those relationship in Django Admin, can post a snippet of that also if you would be interested.
'interests': ['hobby1', 'hobby2', 'hobby3', 'hobby4']
This is basically valid JSON, so you can parse this easily on your end.

Django rest framework: automatically create a url for each field of a model

I have large table of data (~30 Mb) that I converted into into a model in Django. Now I want to have access to that data through a REST API.
I've successfully installed the Django REST framework, but I'm looking for a way to automatically create a URL for each field in my model. My model has about 100 fields, and each field has about 100,000 entries.
If my model is named Sample,
models.py
class Sample(models.Model):
index = models.IntegerField(primary_key=True)
year = models.IntegerField(blank=True, null=True)
name = models.TextField(blank=True, null=True)
...97 more fields...
then I can access the whole model using Django REST framework like this:
urls.py
class SampleSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Sample
fields = ( **100 fields**)
class SampleViewSet(viewsets.ModelViewSet):
queryset = Sample.objects.all()
serializer_class = SampleSerializer
router = routers.DefaultRouter()
router.register(r'sample', SampleViewSet)
But of course my browser can't load all of that data in a reasonable amount of time. I could manually make a different class and URL for each field, but there must be a better way... I want to be able to go to my_site.com/sample/year (for example) and have it list all of the years in JSON format, or my_site.com/sample/name and list all the names, etc.
Please help me figure out how to do this, thanks!
You might be able to do that using a custom viewset route.
You have this:
class ModelViewSet(ModelViewSet):
#list_route()
def sample_field(self, request):
desired_field = request.data.get('field', None)
if not desired_field:
return response # pseudocode
values = Model.objects.all().values_list(desired_field, flat=True)
# serialize this for returning the response
return Response(json.dumps(values)) # this is an example, you might want to do something mode involved
You will be able to get this from the url:
/api/model/sample_field/?field=foo
This extra method on the viewset will create a new endpoint under the samples endpoint. Since it's a list_route, you can reach it using /sample_field.
So following your code, it would be:
mysite.com/sample/sample_field/?field='year'
for example.
There are many interesting details in your question, but with this sample I think you might able to achieve what you want.
Try to use pagination. You can do it in almost the same way as in you question. Pagination in django lets you divide the results into pages. You don't have to display all the entries in the same page. I think this is the best option for you.
Refer django documentation on pagination:
Pagination in django

Modify data before validation step with django rest framework

I have a simple Model that stores the user that created it with a ForeignKey. The model has a corresponding ModelSerializer and ModelViewSet.
The problem is that when the user submits a POST to create a new record, the user should be set by the backend. I tried overriding perform_create on the ModelViewSet to set the user, but it actually still fails during the validation step (which makes sense). It comes back saying the user field is required.
I'm thinking about overriding the user field on the ModelSerializer to be optional, but I feel like there's probably a cleaner and more efficient way to do this. Any ideas?
I came across this answer while looking for a way to update values before the control goes to the validator.
This might be useful for someone else - here's how I finally did it (DRF 3) without rewriting the whole validator.
class MyModelSerializer(serializers.ModelSerializer):
def to_internal_value(self, data):
data['user'] = '<Set Value Here>'
return super(MyModelSerializer, self).to_internal_value(data)
For those who're curious, I used this to round decimal values to precision defined in the model so that the validator doesn't throw errors.
You can make the user field as read_only.
This will ensure that the field is used when serializing a representation, but is not used when creating or updating an instance during deserialization.
In your serializers, you can do something like:
class MyModelSerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
extra_kwargs = {
'user' : {'read_only' : True} # define the 'user' field as 'read-only'
}
You can then override the perform_create() and set the user as per your requirements.
Old topic but it could be useful for someone.
If you want to alter your data before validation of serializer:
serializer.initial_data["your_key"] = "your data"
serializer.is_valid(raise_exception=True)