Overriding the UserDetailsSerializer in django-rest-auth - python

I am trying to modify the UserDetailsSerializer with a customized User Detail, but when the server is running, it throws an error of " 'User' object has no attribute 'user'". I tried a lot of methods and none of them work.
My code is:
mode.py
class userProfileModel(models.Model):
GENDER = [
('', ""),
('M', "Male"),
('F', "Female")
]
user = models.OneToOneField(User, related_name='userprofile', on_delete=models.CASCADE, default='')
age = models.DateField(auto_now_add=True)
gender = models.CharField(max_length=10, choices=GENDER, default='')
phone = models.IntegerField(default=0)
user_is_active = models.BooleanField(default=False)
def __str__(self):
return self.user.username
And serializers.py
from rest_auth.serializers import UserDetailsSerializer
from rest_framework import serializers
from .models import userProfileModel
class Profile(serializers.ModelSerializer):
user = UserDetailsSerializer()
class Meta:
model = userProfileModel
fields = ('user',)
class UserDetailsSerializer(UserDetailsSerializer):
profile = Profile(many=True)
class Meta(UserDetailsSerializer.Meta):
fields = UserDetailsSerializer.Meta.fields + ('profile',)
read_only_fields = ('',)
And settings.py
REST_AUTH_SERIALIZERS = {
'USER_DETAILS_SERIALIZER': 'app.serializers.UserDetailsSerializer',
}
When I run this code, it throws this error.
Got AttributeError when attempting to get a value for field profile on serializer UserDetailsSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the User instance.
Original exception text was: 'User' object has no attribute 'profile'.

use source='userprofile' as
class UserDetailsSerializer(UserDetailsSerializer):
profile = Profile(source='userprofile')
class Meta(UserDetailsSerializer.Meta):
fields = UserDetailsSerializer.Meta.fields + ('profile',)
read_only_fields = ('',)
Since User and userProfileModel in a OneToOne relation, you don't want to specify the many=True argument in the serializer

Related

Get all post from users which my user follow

I would like to get all posts of all users which my user follow.
My User model looks like
from django.db import models
from django.contrib.auth.models import AbstractUser
from apps.friend_request.models import FriendRequest
# Save avatar to user specific directory in media files
def user_avatar_directory(instance, filename):
return f'{instance.username}/avatar/{filename}'
class User(AbstractUser):
# Field that is used as the unique identifier
USERNAME_FIELD = 'email'
# Fields that are required when using createsuperuser (username_field and password fields are required by default)
REQUIRED_FIELDS = ['username', 'first_name', 'last_name']
# Fields that shall be treated as public and can be exposed to all logged-in users
PUBLIC_FIELDS = ('id', 'username', 'first_name', 'last_name', 'country')
email = models.EmailField(unique=True)
first_name = models.CharField(max_length=150)
last_name = models.CharField(max_length=150)
country = models.CharField(max_length=150, blank=True, null=True)
city = models.CharField(max_length=150, blank=True, null=True)
about = models.TextField(blank=True, null=True)
avatar = models.ImageField(upload_to=user_avatar_directory, blank=True, null=True)
followers = models.ManyToManyField(to='self', symmetrical=False, related_name='followees', blank=True)
my post model
from django.db import models
from django.conf import settings
class Post(models.Model):
user = models.ForeignKey(
#to=User,
to=settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='posts',
#null=True
)
content = models.CharField(
max_length=150,
blank=False
)
created = models.DateTimeField(
auto_now=True
)
liked_by = models.ManyToManyField(
#to=User,
to=settings.AUTH_USER_MODEL,
related_name='liked_posts',
blank=True
)
# TODO
# comments = Set('Comment')
# TODO sharing not yet clear what it is about
# shared = Optional('Post', reverse='sharing')
# sharing = Set('Post', reverse='shared')
def __str__(self):
return f'ID: {self.pk}: {self.content} '
class Post_Pic(models.Model):
created = models.DateTimeField(
auto_now=True
)
post_id = models.ForeignKey(
to=Post,
on_delete=models.CASCADE,
related_name='posts',
)
image = models.ImageField(
upload_to='post_pic'
)
def __str__(self):
return f'ID: {self.pk} Post: {self.post_id} File: {self.image.name}'
my views.py
class MyFollowersPosts(ListView):
"""
Get all followers
"""
queryset = Post.objects.all()
serializer_class = FollowesSerilizer
def get_queryset(self):
posts = []
for user in self.request.user.followers.all():
for post in Post.objects.filter(author=user.followed):
posts.append(post)
return posts
Problem is that I am always getting this error and I can´t find out where is problem
AttributeError at /backend/api/social/posts/following/
'AnonymousUser' object has no attribute 'followers'
Request Method: GET
Request URL: http://127.0.0.1:8000/backend/api/social/posts/following/
Django Version: 3.1.4
Exception Type: AttributeError
Exception Value:
'AnonymousUser' object has no attribute 'followers'
Exception Location: C:\Users\Dell\anaconda3\envs\motion-backend\lib\site-packages\django\utils\functional.py, line 241, in inner
Python Executable: C:\Users\Dell\anaconda3\envs\motion-backend\python.exe
Python Version: 3.9.1
Python Path:
['C:\Users\Dell\Desktop\day-5-django-motion-assignment',
'C:\Users\Dell\anaconda3\envs\motion-backend\python39.zip',
'C:\Users\Dell\anaconda3\envs\motion-backend\DLLs',
'C:\Users\Dell\anaconda3\envs\motion-backend\lib',
'C:\Users\Dell\anaconda3\envs\motion-backend',
'C:\Users\Dell\anaconda3\envs\motion-backend\lib\site-packages']
Server time: Mon, 04 Jan 2021 13:53:46 +0000
Your get_queryset should return a QuerySet, not a list. You furthermore do not need to obtain items with a loop, you can query with:
from rest_framework.permissions import IsAuthenticated
class MyFollowersPosts(ListView):
# …
permission_classes = [IsAuthenticated]
def get_queryset(self):
return Post.objects.filter(author__followees=self.request.user)
Furthermore the user should be logged in. You do this with the IsAuthenticated permission class.
You can use something like this :
user = request.user
followings_posts = Post.objects.filter(user__in = user.followees.all())

How do I do a post request with related serializers Django rest framework

I have model Package with Supplier and PackageSize, foreign keys and a field to which is also a foreign key of a Shipping model which contains where the supplier wants to ship the package so to make sure I a user can submit the whole information in one request, I created my serializers and linked them like such.
serializers.py
from users.models import Supplier
from packages.models import Package , PackageSize , ShippingLocation , Shipping
class ShippingLocationSerializer(serializers.ModelSerializer):
class Meta:
model= ShippingLocation
fields = ['latitude','longitude']
def create(self, validated_data):
return ShippingLocation(**validated_data)
class PackageSizeSerializer(serializers.ModelSerializer):
class Meta:
model= PackageSize
fields = ['length', 'width' ,'height' ,'weight']
def create(self, validated_data):
"""
docstring
"""
pass
class ShippingSerializer(serializers.ModelSerializer):
location = ShippingLocationSerializer(source='location_set')
class Meta:
model = Shipping
fields = [
'first_name',
'last_name',
'phone_number',
'email',
'street_address',
'village',
'district',
'country',
'location'
]
def create(self, validated_data):
"""
docstring
"""
pass
class SupplierPackageSerializer(serializers.ModelSerializer):
size = PackageSizeSerializer(source='size_set')
shipping_location= ShippingSerializer(source='to_set')
class Meta:
model = Package
fields = ['supplier', 'name', 'size', 'shipping_location', ]
read_only_fields = ['supplier']
def create(self, validated_data):
user =Supplier.objects.get(username=self.context['request'].user)
return Package(supplier=user, **validated_data )
and created my views like such
view.py
from rest_framework import generics
from packages.models import Package
from .serializers import , SupplierPackageSerializer
from rest_framework.permissions import IsAuthenticated
class SupplierPackageViewSet(generics.ListCreateAPIView):
serializer_class = SupplierPackageSerializer
queryset = Package.objects.all().order_by('-name')
permission_classes = [IsAuthenticated]
models.py
from django.db import models
from django.conf import settings
from users.models import Supplier
class ShippingLocation(models.Model):
latitude = models.IntegerField()
longitude = models.IntegerField()
class Shipping (models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
phone_number = models.CharField(max_length=14)
email = models.EmailField()
street_address = models.CharField(max_length=30)
village = models.CharField(max_length=30)
district = models.CharField(max_length=30)
country = models.CharField(max_length=30)
location = models.OneToOneField(ShippingLocation , default=None, on_delete=models.CASCADE )
# Returns the string representation of the model.
def __str__(self):
return self.email
class PackageSize(models.Model):
length = models.IntegerField()
width = models.IntegerField()
height = models.IntegerField()
weight = models.IntegerField()
# Create your models here.
class Package(models.Model):
TYPE = (
('1', 'Envelope'),
('2', 'Parcel'),
('2', 'Soft'),
('2', 'Freezed'),
)
TYPE = (
('1', 'CREATED IN SYSTEM'),
('2', ''),
('2', 'Soft'),
('2', 'Freezed'),
)
name = models.CharField(max_length=30)
supplier = models.ForeignKey( Supplier , on_delete=models.DO_NOTHING)
to = models.ForeignKey(Shipping, default=None, on_delete=models.CASCADE )
size = models.ForeignKey( PackageSize, default=None, on_delete=models.CASCADE )
type = models.CharField(max_length=1, default="1", choices=TYPE)
def __str__(self):
return self.name
the challenge is when I submit the data it is validated very well but it cannot be saved and I get this error
TypeError at /supplier/package
Package() got an unexpected keyword argument 'size_set'
Model Package has foreign key to PackageSize ( only one size per package) so source is not size_set but just size
size = PackageSizeSerializer()
EDIT:
You will also have to override create method on serializer to save related object as documented in writable nested serializer
Something in a line of
def create(self, validated_data):
size_data= validated_data.pop('size', None)
if size_data:
package_size= PackageSize.objects.get_or_create(**size_data)[0]
validated_data['size'] = package_size
return Package.objects.create(**validated_data)
EDIT!
serializers.py
class StringSerializer(serializers.StringRelatedField):
def to_internal_value(self, value):
return value
class SupplierPackageSerializer(serializers.ModelSerializer):
item1 = StringSerializer()
item2 = StringSerializer()
size = serializers.SerializerMethodField()
shipping_location= serializers.SerializerMethodField()
class Meta:
model = Package
fields = ['supplier', 'name', 'size', 'shipping_location', ]
read_only_fields = ['supplier']
# add this
def get_size(self, obj):
return PackageSizeSerializer(obj.item1).data
def get_shipping_location(self,obj):
return ShippingSerializer(obj.item2).data
DOCS:
https://www.django-rest-framework.org/api-guide/fields/#serializermethodfield
https://www.django-rest-framework.org/api-guide/relations/#stringrelatedfield
So basically The StringSerializer() class is a way to return a string representation of the model data.
While SerializerMethodField This is a read-only field. It gets its value by calling a method on the serializer class it is attached to. It can be used to add any sort of data to the serialized representation of your object.

Django Rest - Serializer: must be a instance on create

I'm trying to create create a nested serializer using the Django Rest framework. The relationship is Profile X User but when i use Profile.objects.create(user=profile, **user_data) i get ValueError: Cannot assign "<Profile: Profile object (7)>": "Profile.user" must be a "User" instance..
This should be some rookie misunderstanding of models relationship definitions or the serializer declaration itself but I can't find anything around the docs. If someone can point me a direction I'll be gracefull.
models.py
class User(models.Model):
name = models.CharField(max_length=100, blank=False)
email = models.CharField(max_length=100, blank=True, default='')
password = models.CharField(max_length=100, blank=True, default='')
timestamp = models.DateTimeField(default= timezone.now)
class Meta:
ordering = ['timestamp']
class Profile(models.Model):
# choices [...]
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True)
profile_type = models.CharField(max_length=2,choices=PROFILE_CHOICES,default=TEAMMEMBER)
authentication_token = models.CharField(max_length=100, null=True)
avatar_url = models.CharField(max_length=100, default='')
permissions = models.CharField(max_length=100, null=True)
timestamp = models.DateTimeField(default= timezone.now)
class Meta:
ordering = ['timestamp']
serializer.py
class UserSerlializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['name', 'email', 'password']
class ProfileSerializer(serializers.ModelSerializer):
user = UserSerlializer()
class Meta:
model = Profile
fields = ['user', 'profile_type']
def create(self, validated_data):
user_data = validated_data.pop('user')
profile = Profile.objects.create(**validated_data)
Profile.objects.create(user=profile, **user_data)
return Profile
POST
{
"profile_type" : "ST",
"user": {
"name" : "test",
"email" : "test#test.com",
"password" : "123456"
}
}
You are creating instances in wrong way. Change your create(...) method as,
class ProfileSerializer(serializers.ModelSerializer):
user = UserSerlializer()
class Meta:
model = Profile
fields = ['user', 'profile_type']
def create(self, validated_data):
user_data = validated_data.pop('user')
user_instance = User.objects.create(**user_data)
profile_instance = Profile.objects.create(user=user_instance, **validated_data)
return profile_instance
Profile.user should beUser instance, but you are assigning Profile instance.
Change your create method to this:
class ProfileSerializer(serializers.ModelSerializer):
user = UserSerlializer()
class Meta:
model = Profile
fields = ['user', 'profile_type']
def create(self, validated_data):
user_data = validated_data.pop('user')
profile = Profile.objects.create(**validated_data)
user = User.objects.create(**user_data) # 1. creating user
profile.user = user # 2. assigning user
profile.save() # 3. saving profile after adding user
return profile # returning Profile instance.
inherit your user model from django contrib auth module also, and make a one to one relation with profile
from django.contrib.auth.models import User

Unable to POST JSON data from multiple select element with Django REST Framework

I would like to be able to send an AJAX POST request to my API endpoint to create a new instance of my Asset model with multiple Category instances referenced in my Asset model, hence the many-to-many field type in my Asset model.
I'm able to successfully POST and create new Asset instances, however my category field won't accept any data at all. The category field remains empty when a new Asset instance is created. I think it has something to do with my CategorySerializer. I'm still learning how to use Django REST Framework so I'd appreciate if I could get some help figuring out how to work with serializers in Django REST Framework.
I've already tried modifying the AssetSerializer create method to handle parsing the JSON and validating the data but that hasn't worked. I've also tried other solutions suggested in other posts I've found on StackOverflow but haven't found anything that works for my situation.
Here's my serializers.py file:
class CategorySerializer(serializers.ModelSerializer):
name = serializers.CharField(required=False, read_only=True)
class Meta:
model = Category
fields = ('id', 'name')
class AssetSerializer(serializers.ModelSerializer):
name = serializers.CharField(allow_null=True)
description = serializers.CharField(allow_null=True)
manufacturer = serializers.CharField(allow_null=True)
uid = serializers.UUIDField(read_only=True, allow_null=True)
borrower = BorrowerSerializer(allow_null=True, read_only=True)
condition = serializers.ChoiceField(choices=Asset.CONDITION_TYPE, default='g', allow_null=True)
owner = serializers.ReadOnlyField(source='owner.username')
return_date = serializers.DateField(allow_null=True)
checked_out = serializers.BooleanField(allow_null=True)
category = CategorySerializer(required=False, many=True)
class Meta:
model = Asset
fields = ('uid',
'name',
'manufacturer',
'model',
'description',
'owner',
'condition',
'category',
'borrower',
'checked_out',
'return_date',
'is_dueback',
)
def update(self, instance, validated_data):
instance.borrower = validated_data.get('borrower', instance.borrower)
instance.return_date = validated_data.get('return_date', instance.return_date)
instance.checked_out = validated_data.get('checked_out', instance.checked_out)
instance.name = validated_data.get('name', instance.name)
instance.manufacturer = validated_data.get('manufacturer', instance.manufacturer)
instance.model = validated_data.get('model', instance.model)
instance.description = validated_data.get('description', instance.description)
instance.condition = validated_data.get('condition', instance.condition)
instance.category = validated_data.get('category', instance.category)
instance.save()
return instance
def create(self, validated_data):
return Asset.objects.create(**validated_data)
Here's my Asset model:
class Asset(models.Model):
"""Model representing an Asset"""
uid = models.UUIDField(primary_key=True, default=uuid.uuid4)
name = models.CharField(max_length=200)
manufacturer = models.CharField(max_length=64)
model = models.CharField(max_length=128)
description = models.TextField()
category = models.ManyToManyField(Category)
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
borrower = models.ForeignKey(Borrower, on_delete=models.CASCADE, null=True, blank=True)
checked_out = models.BooleanField(default=False)
return_date = models.DateField(null=True, blank=True)
CONDITION_TYPE = (
('e', 'Excellent'),
('g', 'Good'),
('f', 'Fair'),
('p', 'Poor'),
)
condition = models.CharField(
max_length=1,
choices=CONDITION_TYPE,
blank=True,
help_text='Asset condition')
class Meta:
ordering = ['return_date']
#property
def is_dueback(self):
if self.return_date and date.today() > self.return_date:
return True
return False
def display_category(self):
"""Create a string for the Category. This is required to display category in Admin."""
return ', '.join(category.name for category in self.category.all())
display_category.short_description = 'Category'
def __str__(self):
return f'{self.uid} - {self.name}'
def get_absolute_url(self):
return reverse('asset-detail', args=[str(self.uid)])
Here's my Category model:
class Category(models.Model):
"""Model representing an Asset category"""
name = models.CharField(max_length=128)
def __str__(self):
return self.name
I'd appreciate any help you could provide. Thank you in advance.
i'm almost new in DRF but i try to help. why you writing all the field in serializer when you using ModelsSerializer? not need to telling ModelSerializer what type of field should be because you are pointing to model in class Meta and DRF know about fields and type and etc . second about allow_null=True in serializer, when Model haven't null=True you can't except DRF can create a not null-able field for instance with null=True so if you wnt a field can be null just add null=True in Model class . for your problem about ManytoMantry field try to use Primary key relation for ManyToMany fields in your serializers then pass id of Category instances in list:
class AssetSerializer(serializers.ModelSerializer):
borrower = BorrowerSerializer(allow_null=True, read_only=True)
category = serializers.PrimaryKeyRelatedField(many=True, queryset=Category.objects.all())
class Meta:
model = Asset
fields = ('uid',
'name',
'manufacturer',
'model',
'description',
'owner',
'condition',
'category',
'borrower',
'checked_out',
'return_date',
'is_dueback',
)
read_only_fields = ( 'uid' , ) # this fields will be read_only
depending on how you using this serializer in your view for save and update have difference way. if your view is generics class so will do create and update itself by POST and PUT method .and for other class view that isn't belong to generics DRF view you can using serializer.save() to create a new instance.wish help you.
pass data something like:
{
"name" : "foo",
"manufacture" : "foo",
.
.
.
"category" : [1,2,3,24,65]
}

Getting attribute error when trying to access the nested serializer in Django Rest Framework

I am getting following error while using the PostSerializer:
Got AttributeError when attempting to get a value for field
full_name on serializer UserSerializer. The serializer field might
be named incorrectly and not match any attribute or key on the long
instance. Original exception text was: 'long' object has no attribute
'full_name'.
Serializers are as follows:
class PostSerializer(serializers.ModelSerializer):
author = UserSerializer(required=False, allow_null=True)
class Meta:
model = Post
fields = ('id', 'author', 'message', 'rating', 'create_date', 'close_date',)
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'full_name',)
View:
class PostMixin(object):
model = Post
serializer_class = PostSerializer
permission_classes = [
PostAuthorCanEditPermission
]
queryset = model.objects.all()
def pre_save(self, obj):
"""Force author to the current user on save"""
obj.author = self.request.user
return super(PostMixin, self).pre_save(obj)
class PostList(PostMixin, generics.ListCreateAPIView):
pass
User model:
class User(AbstractBaseUser):
email = models.EmailField(unique=True)
username = models.CharField(max_length=40, unique=True, null=True)
full_name = models.CharField(max_length=50, blank=False)
phone = models.CharField(max_length=20, unique=True, null=True)
about = models.CharField(max_length=255, blank=True)
type = models.CharField(max_length=1, default='U')
is_active = models.BooleanField(default=True)
date_joined = models.DateTimeField(auto_now_add=True)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['full_name']
def __unicode__(self):
return self.email
def get_full_name(self):
return self.full_name
def get_short_name(self):
return self.full_name
Problem
Got AttributeError when attempting to get a value for field full_name on serializer UserSerializer.
The model User in Django has no such field called full_name.
There is though a method get_full_name() that does what you want.
Solution
So try using it through a SerializerMethodField
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username') # no full_name here
full_name = serializers.SerializerMethodField('get_full_name')
This will add a field called full_name to your serialized object, with the value pulled from User.get_full_name()
Check you are using your custom model and not Django's User model
You've customized your own User model, but since that models has full_name, you shouldn't have gotten that error in the first place, so double check you are not referencing Django's default User model first.
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User # <--- Make sure this is your app.models.User,
# and not Django's User model
fields = ('id', 'username', 'full_name',) # This is OK on your User model
or just
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'first_name', 'last_name')

Categories

Resources