Expected a dictionary, but got str Django Rest Framework - django

Right now I am creating a user department with a list of users that are a foreign key back to the main user model. I had this working yesterday, but for some reason I screwed it up. I imagine it has something to do with the serializers. I want to be able to post a list of users in this format
['jack', 'tom']
However, even using the raw data api this is not allowing me to do this. Here is my code:
Serializers:
class DepartmentSerializer(serializers.ModelSerializer):
user_department = UserSerializer(many=True)
class Meta:
model = Departments
fields = '__all__'
class DepartmentUpdateSerializer(serializers.ModelSerializer):
user_department = UserSerializer(many=True)
class Meta:
model = Departments
fields = ['department_name', 'department_head', 'user_department']
I swear yesterday it was allowing me to select from a list of users in the api. I could also post and it would work from the front end. However, now whenever I create a department it's expecting a dictionary, which I am not trying to pass.

Dudes, for whatever reason, removing () after the UserSerializer fixed it. If anyone can explain why that would be even better!
class DepartmentSerializer(serializers.ModelSerializer):
user_department = UserSerializer
class Meta:
model = Departments
fields =['department_name', 'department_head', 'user_department']
class DepartmentUpdateSerializer(serializers.ModelSerializer):
user_department = UserSerializer
class Meta:
model = Departments
fields = ['department_name', 'department_head', 'user_department']

When you use the nested serializer you need to add the nested serializer field (user_department in your case) to the fields too, as you can see you used
fields = '__all__'
which does not include your nested serializer field, you need to manually add that to the meta fields

Related

Need to fetch two specific field values from one model to another seperately in django API rest framework

model 1
class Users(models.Model):
employee_name = models.CharField(max_length=50)
dob=models.DateField(max_length=8)
email=models.EmailField(max_length=254,default=None)
pancard=models.CharField(max_length=25,default=None)
aadhar=models.CharField(max_length=20,default=None)
personal_email_id=models.EmailField(max_length=254,default=None)
phone = PhoneField(blank=True)
emergency_contact_no=models.IntegerField(default=None)
emergency_contact_name=models.CharField(max_length=100,null=True)
relation=models.CharField(max_length=25,default=None)
blood_group=models.CharField(max_length=25,choices=BLOOD_GROUP_CHOICES,null=True)
desingnation=models.ForeignKey(Designation,on_delete=CASCADE,related_name="desingnation")
billable_and_non_billable=models.CharField(max_length=25,choices=BILLABLE_and_NON_BILLABLE_CHOICES,default='Billable')
joining_date=models.DateField(max_length=15,null=True)
relieving_date=models.DateField(max_length=15,null=True)
def __str__(self):
return self.employee_name
model 2
class Consolidated(models.Model):
emp_name=models.ForeignKey(Users,on_delete=CASCADE)
proj_name=models.ForeignKey(Project,on_delete=CASCADE)
custom_name=models.ForeignKey(Client,on_delete=CASCADE)
Cons_date=models.ForeignKey(Add_Timelog,on_delete=CASCADE)
bill_no_bill=models.ForeignKey(Users,on_delete=CASCADE,related_name="billable_and_non_billable+")
def __str__(self):
return str(self.emp_name)
Serializers
class UserSerializers(serializers.ModelSerializer):
class Meta:
model= Users
fields = '__all__'
class Consolidated_serializers(serializers.ModelSerializer):
class Meta:
model=Consolidated
fields= '__all__'
Viewsets
class UserViewset(viewsets.ModelViewSet):
permission_classes=(permissions.IsAdminUser,)
queryset=models.Users.objects.all()
serializer_class=serializers.UserSerializers
class Consolidated_ViewSet(viewsets.ModelViewSet):
permission_classes=(permissions.IsAdminUser,)
queryset=models.Consolidated.objects.all()
serializer_class=serializers.Consolidated_serializers
Actually I was stucked in the middle, as I need to take the values from 'billable_and_non_billable' field from the Users model and display those values under Consolidated model bill_no_bill field. With the above code I can only take the employee_name values from the Users model to the emp_name of Consolidated model and the same value is getting displayed in the bill_no_bill field. Please help me find any ways for this problem as I am new to this Django. Basically its needs to be a API which operates GET method.
You are getting same value because you are using Foreign Key to the user model for both emp_name and bill_no_bill. Foreign keys are used for many to one relations between models.
See: https://docs.djangoproject.com/en/4.0/topics/db/examples/many_to_one/
So your Consolidated Model should be:
class Consolidated(models.Model):
employee=models.ForeignKey(Users,on_delete=CASCADE)
project=models.ForeignKey(Project,on_delete=CASCADE)
custom_name=models.ForeignKey(Client,on_delete=CASCADE)
cons_date=models.ForeignKey(Add_Timelog,on_delete=CASCADE)
To get the values of these fields you can use Nested Objects Representation in DRF serializers.
So the consolidated serializer becomes:
class ConsolidatedSerializer(serializers.ModelSerializer):
employee = UserSerializer()
class Meta:
model = Consolidated
fields = ['employee', 'project', 'custom', 'date']
The bill_no_bill field would be part of the returned object in viewset.
Alternatively you can override the to_representation method of serializer or SerializerMethodField

Conditionally nest Django serializers

So, I have a foreign key to my User model in many of my models. Now, the serializers for these models are nested, in that they include the entire user object rather than just the id. I have done so as shown bellow:
class BadgeSerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=True)
class Meta:
model = Badge
fields = '__all__'
It works as expected. However, I seldom find myself in a situation where I want just the id. I was wondering what is the best way to conditionally nest my BadgeSerializer...
Now, the best solution I can think of is to have a non-nested BadgeSerializer, which includes only the user id. And then have a NestedBadgeSerializer (extending BadgeSerializer) which does nest the User model and include the entire user object.
class BadgeSerializer(serializers.ModelSerializer):
class Meta:
model = Badge
fields = '__all__'
class NestedBadgeSerializer(BadgeSerializer):
user = UserSerializer(read_only=True)
class Meta:
model = Badge
fields = '__all__'
I am NOT sure if that's the proper way though.

Add a set of related models to serializer output in django w djange-rest-framework

So I am just getting familiar with django and django-rest-framework.
I have a model of Jobs that have an owner, and I want to add the list of jobs a user owns to the user endpoint.
I tried to do it like this, as the tutorial of the django rest framework says:
class UserSerializer(serializers.ModelSerializer):
jobs = serializers.PrimaryKeyRelatedField(many = True, queryset = Job.objects.all())
class Meta:
model = User
fields = ('id', 'username', 'jobs')
But for some reason, when I want to view the users, I recieve
AttributeError at /users/
'User' object has no attribute 'jobs'
The documentation about the PrimareyKeyRelatedField looks just like this. Do I maybe have to do something with the user model?
What works is the following:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'job_set')
but I don't like that solution as I want to have more control about how the list is presented (maybe change the ids to complete json objects or rename the attribut name from 'job_set' to 'job)
I really don't now what I am overseeing...
You can do two things,
jobs = serializers.PrimaryKeyRelatedField(many = True, source='job_set' queryset = Job.objects.all())
or
Set related_name='jobs' attribute to User relational field.

djangorestframework: Filtering in a related field

Basically, I want to filter out inactive users from a related field of a ModelSerializer. I tried Dynamically limiting queryset of related field as well as the following:
class MySerializer(serializers.ModelSerializer):
users = serializers.PrimaryKeyRelatedField(queryset=User.objects.filter(active=True), many=True)
class Meta:
model = MyModel
fields = ('users',)
Neither of these approaches worked for just filtering the queryset. I want to do this for a nested related Serializer class as a field (but couldn't even get it to work with a RelatedField).
How do I filter queryset for nested relation?
I'll be curious to see a better solution as well. I've used a custom method in my serializer to do that. It's a bit more verbose but at least it's explicit.
Some pseudo code where a GarageSerializer would filter the nested relation of cars:
class MyGarageSerializer(...):
users = serializers.SerializerMethodField('get_cars')
def get_cars(self, garage):
cars_queryset = Car.objects.all().filter(Q(garage=garage) | ...).select_related()
serializer = CarSerializer(instance=cars_queryset, many=True, context=self.context)
return serializer.data
Obviously replace the queryset with whatever you want. You don't always need the to give the context (I used it to retrieve some query parameters in the nested serializer) and you probably don't need the .select_related (that was an optimisation).
One way to do this is to create a method on the Model itself and reference it in the serializer:
#Models.py
class MyModel(models.Model):
#...
def my_filtered_field (self):
return self.othermodel_set.filter(field_a = 'value_a').order_by('field_b')[:10]
#Serialziers.py
class MyModelSerialzer(serializers.ModelSerializer):
my_filtered_field = OtherModelSerializer (many=True, read_only=True)
class Meta:
model = MyModel
fields = [
'my_filtered_field' ,
#Other fields ...
]
Another way to avoid the SerializerMethodField solution and therefore still allow writing to the serializer as well would be to subclass the RelatedField and do the filtering there.
To only allow active users as values for the field, the example would look like:
class ActiveUsersPrimaryKeyField(serializers.PrimaryKeyRelatedField):
def get_queryset(self):
return super().get_queryset().filter(active=True)
class MySerializer(serializers.ModelSerializer):
users = ActiveUsersPrimaryKeyField(many=True)
class Meta:
model = MyModel
fields = ('users',)
Also see this response.
Note that this only restricts the set of input values to active users, though, i.e. only when creating or updating model instances, inactive users will be disallowed.
If you also use your serializer for reading and MyModel already has a relation to a user that has become inactive in the meantime, it will still be serialized. To prevent this, one way is to filter the relation using django's Prefetch objects. Basically, you'll filter out inactive users before they even get into the serializer:
from django.db.models import Prefetch
# Fetch a model instance, eagerly prefetching only those users that are active
model_with_active_users = MyModel.objects.prefetch_related(
Prefetch("users", queryset=User.objects.filter(active=True))
).first()
# serialize the data with the serializer defined above and see that only active users are returned
data = MyModelSerializer(model_with_active_users).data

How can I limit fields in ToManyField with full=True in django-tastypie

I have the following resource:
class MachineResource(ModelResource):
manager = fields.ToOneField(UserResource, 'manager',full=True)
class Meta:
queryset = Service.objects.filter(service_type='machine')
resource_name = 'machine'
This works fine. And will return a list of machines, and an embedded user object (the manager) in each.
However, I only one want 2-3 fields from the manager user. I dont want it to contain the managers salted pass and other private data for example.
As far as I can see there isn't a way I can do this easily?
Just take a look at the Quick Start section for django-tastypie. There's a perfect example right there. When you define your ModelResource subclass for User (your "manager"), simply add a Meta class with an exclude attribute set to the list of fields you don't want to show.
class UserResource(ModelResource):
class Meta:
queryset = User.objects.all()
resource_name = 'auth/user'
excludes = ['email', 'password', 'is_superuser']
I personally find the notion of creating two ModelResources for the same Model class a bit inelegant. For instance, suppose you wanted to display the email field in the detail view of UserResource but not while being displayed as a full object as part of the MachineResource. The way I would solve your problem is by deleting the non-required field's key in the data dictionary of the embedded object in the dehydrate method. A bit hacky way maybe, but works fine for me. For your case, you can do:
class MachineResource(ModelResource):
manager = fields.ToOneField(UserResource, 'manager',full=True)
class Meta:
queryset = Service.objects.filter(service_type='machine')
resource_name = 'machine'
def dehydrate(self,bundle):
del bundle.data['manager'].data['email']
return bundle