Django Rest Framework - Serialize multiple models - django

I have two models, the default User model and a UserProfile model that extends the user model:
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='UserProfile')
universidad = models.ForeignKey(Universidad, related_name ='universidad')
group_admin = models.ManyToManyField(Group, related_name = 'group_admin')
I'm trying to obtain a user field that allows to GET, PUT and POST data with this format:
'username' = 'foo'
'password' = 'password'
'email' = 'a#b.com'
'universidad' = 'Harvard'
'group_admin' = [1] #list of groups id
I have used nested serializers but it's read only.
I also proved this solution, but i obtain a KeyError: 'universidad'.
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('url', 'username', 'password', 'email','universidad', 'group_admin')
write_only_fields = ('password',)
Any help would be appreciated.

The KeyError occurs because you are trying to use the reverse relationship of UserProfile in the serializer. Reverse relationships aren't automatically included in HyperLinkedModelSerializers. See the docs for more information and try:
fields = ('url', 'username', 'password', 'email','UserProfile__universidad', 'group_admin')

Related

Is it possible to add more fields to admin/auth/user?

models.py
class UserProfile(User):
bio = models.TextField(blank=True, null=True)
pfp = models.ImageField(verbose_name='Profile Picture', blank=True, null=True, upload_to="images/profile")
forms.py
class UserRegisterForm(UserCreationForm):
email = forms.EmailField(widget=forms.EmailInput(attrs={"class":"form-control", "placeholder":"example#example.com"}))
first_name = forms.CharField(widget=forms.TextInput(attrs={"class":"form-control"}))
last_name = forms.CharField(widget=forms.TextInput(attrs={"class":"form-control"}))
pfp = forms.ImageField(required=False, widget=forms.FileInput(attrs={"class":"form-control"}))
bio = forms.CharField(required=False, widget=forms.Textarea(attrs={"class":"form-control", 'rows':5, "placeholder":"Write something about yourself..."}))
class Meta:
model = UserProfile
fields = ['first_name', 'last_name', 'username', 'email', "pfp", "bio"]
When creating the user, the two newly added fields (bio and pfp) are not being saved in admin/auth/user, so my question is, is it possible to add those fields to the admin users database?
views.py
class SignUpView(CreateView):
form_class = UserRegisterForm
template_name = "registration/signup.html"
success_url = reverse_lazy("login")
are not being saved in admin/auth/user
Indeed, these are safed on the UserProfile model.
You thus can make a ModelAdmin for this:
# app_name/admin.py
from django.contrib import admin
from app_name.models import UserProfile
#admin.register(UserProfile)
class AuthorAdmin(admin.ModelAdmin):
list_display = ('first_name', 'last_name', 'username', 'email', 'pfp', 'bio')
Then the details can be seen in the admin/app_name/userprofile section.
Create a custom user model by inheriting the AbstractUser and adding extra fields needed. After that, register that custom user model by assigning it to AUTH_USER_MODEL.
Check here for detailed implementation.

Django Rest Framework : how to specify a serializer to use different fields to create, update and get

I have a doubt with the serializers and so far I have not been able to solve it. I explain the doubt with the following example:
I have a User model and this model has the following attributes: username, password, first_name, last_name, age, gender. I also have a serializer, which is called UserSerializer. The UserSerializer should do the following:
When inserting information into the database, UserSerializer should only take into account the fields: username, password, first_name, last_name.
When retrieving information from the database, UserSerializer should only take into account the fields: age, gender.
When updating the database information, UserSerializer should only take into account the fields: password.
My solution:
class UserSerializer:
class UserCreateSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username', 'password', 'first_name', 'last_name']
class UserGetSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['age', 'gender']
class UserUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['password']
Question:
The question: is there any way to synthesize the three serializers into one, or what is the best solution for this problem?
Thank you very much.
Use write_only and read_only parameters in serializer fields to specify fields for creating and retrieving
class UserSerializer(serializers.ModelSerializer):
username = serializers.CharField(write_only=True, required=False)
password = serializers.CharField(write_only=True)
first_name = serializers.CharField(write_only=True, required=False)
last_name = serializers.CharField(write_only=True, required=False)
age = serializers.IntegerField(read_only=True)
gender = serializers.CharField(read_only=True)
class Meta:
model = User
fields = ['username', 'password', 'first_name', 'last_name', 'age', 'gender']
If any field is optional, then you can add the required=False

How to Show Foreign Key data in Django rest framework?

Model:
class Demo(models.Model):
name = models.CharField(max_length=255)
desc = models.TextField()
user = models.ForeignKey('auth.User', on_delete=models.CASCADE)
Serializer:
class DemoSerializer(serializers.ModelSerializer):
class Meta:
model = Demo
fields = '__all__'
I have form in frontend side where I'm adding name, desc and assigning to User so here I'm getting on an issue.
I'm passing data to API {name: "demo", desc: "lorem ipsum", user: 1 }
It's working on save but after saving it's return same response but I want user first_name, last_name, and email in return response.
Because I have a table showing a list of demo table content. but always getting only User ID not a detail of user.
If I'm increasing depth of Serializer It's creating an issue in save time but on get records time I'm getting all details of User model. Like Password also in response so that is a security issue for me show all thing.
You can use depth = 1 to get all the data of foreign key object:
class DemoSerializer(serializers.ModelSerializer):
class Meta:
model = Demo
fields = '__all__'
depth = 1
You could separate the Create and Retrieve serializer. For example, the create serializer will be the one you are currently using:
class DemoCreateSerializer(serializers.ModelSerializer):
class Meta:
model = Demo
fields = '__all__'
On the other hand, Retrieve serializer will serialize the User with a different serializer using Nested Relationship.
class DemoRetrieveSerializer(serializers.ModelSerializer):
user = UserMinimalSerializer # or you could use your UserSerializer, see the last part of the answer
class Meta:
model = Demo
fields = ('id', 'name', 'desc', 'user')
read_only = ('id', 'name', 'desc', 'user',)
In your view, you will create the data with the first serializer and respond with the second. An example using APIView:
class DemoView(APIView):
def post(self, request, format=None):
create_serializer = DemoCreateSerializer(data=request.data)
if create_serializer.is_valid():
instance = create_serializer.save()
retrive_serializer = DemoRetrieveSerializer(instance)
return Response(retrive_serializer.data, status=status.HTTP_201_CREATED)
return Response(create_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
You might have to customize DRF provided views to achieve this, i.e. for Generic views.
Since you don't want to include all the fields of User model, you will have to write a minimal representation of User using another serializer.
class UserMinimalSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'first_name', 'last_name', 'email')
read_only = ('id', 'first_name', 'last_name', 'email',)
Hope it helps.

Hide nested field from result

I'm using django-rest-framework. I have a serializer with nested data and I want to hide a specific field (password):
class MyUser(models.Model):
# django's auth model
user = models.OneToOneField(User)
class MyUserSerializer(serializers.ModelSerializer):
username = serializers.CharField(source="user.username")
password = serializers.CharField(source="user.password")
# Other fields related to MyUser model
class Meta:
model = MyUser
fields = ( ..., "password")
write_only_fields = ("password",)
The first problem is that if I remove password from fields it will error saying that I have password defined but it's not found in the fields list.
write_only_fields does not having any effect on password; it's always returned.
Is there a way to keep the password for write only and remove it from the result?
I solved it by removing write_only_fields and modified the field itself to write_only:
password = serializer.CharField(source="user.password", write_only=True).
I have no idea why write_only_fields and extra_kwargs did not work.
It didn't work because the write_only_fields attribute of Meta class only overrides the implicit fields (the ones that are only listed in the Meta class fields attributes, and not defined in the ModelSerializer scope) write_only attribute to be True. If you declare a ModelSerializer field explicitly you must define all the attributes that are not default for it to work.
The right code should be something like:
class MyUser(models.Model):
# django's auth model
user = models.OneToOneField(User)
class MyUserSerializer(serializers.ModelSerializer):
username = serializers.CharField(source="user.username")
password = serializers.CharField(source="user.password", write_only=True)
# Other fields related to MyUser model
class Meta:
model = MyUser
fields = ( ..., "password")
write_only_fields = ("password",)
You can also override the function that builds the nested fields. Good choice for when you want to display the default auth_user's name in a nested ListView.
from rest_framework.utils.field_mapping import get_nested_relation_kwargs
def build_nested_field(self, field_name, relation_info, nested_depth):
"""
Create nested fields for forward and reverse relationships.
"""
class NestedSerializer(serializers.ModelSerializer):
class Meta:
model = relation_info.related_model
depth = nested_depth - 1
fields = ['username'] # Originally set to '__all__'
field_class = NestedSerializer
field_kwargs = get_nested_relation_kwargs(relation_info)
return field_class, field_kwargs

Adding more fields or linking user to person

Hello I'm trying to learn django and django-restful-framework.
I was wondering can I add more fields to User(contrib.auth) like so
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('url', 'username', 'firstName', 'lastName', 'ssn', 'email',
'phone', 'jobTitle','image', 'isActive','groups')
This gives me error on firstName. I also tried to tie this with person, but no luck either
class PersonSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.Field(source='owner.username')
class Meta:
model = Person
fields = ('url', 'firstName', 'lastName', 'ssn', 'owner')
class UserSerializer(serializers.HyperlinkedModelSerializer):
persons = serializers.ManyHyperlinkedRelatedField(view_name='person-detail')
class Meta:
model = User
fields = ('url', 'username', 'persons')
I'm trying to make this so that the user can register with more information.
I suggest you head to the Django docs on extending and/or replacing the existing user model.
Once you've got what you want as the model level see if you can serialize that to your needs. (If not post again.)
The field names are lowercase with underscores. E.g it should be first_name and not firstName.