I'm getting an AttributeError when I try to create a nested relation between two serializers. The weird thing is that I'm doing exactly the same thing as with another API, but this time I don't get it working. Here is the code:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
fields = ('id', 'last_login','username', 'created')
class NotificationSerializer(serializers.ModelSerializer):
user_id = UserSerializer()
class Meta:
model = Notification
fields = ('id', 'user_id', 'type', 'parent_id', 'created', 'modified', 'seen')
And the associated models:
class Notification(models.Model):
user = models.ForeignKey(User)
type = models.CharField(max_length=255)
parent_id = models.IntegerField()
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
seen = models.SmallIntegerField(default=0)
def __unicode__(self):
return self.type
class Meta:
db_table = 'notification'
class User(AbstractBaseUser, PermissionsMixin):
username = models.CharField(max_length=255, unique=True)
id = models.IntegerField(primary_key=True)
created = models.DateTimeField(auto_now=True)
tag = models.ManyToManyField(Tag)
USERNAME_FIELD = 'username'
objects = MyUserManager()
class Meta:
db_table = 'user'
The error:
Exception Type: AttributeError
Exception Value:
'long' object has no attribute 'id'
Exception Location: /lib/python2.7/site-packages/rest_framework/fields.py in get_component, line 55
Can anyone help me with this error? A normal primary key relationship works, but I would definitely like to get a nested relationship.
Since your Notification model has a field called user, I think you should use it instead of user_id:
class NotificationSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = Notification
fields = ('id', 'user', 'type', 'parent_id', 'created', 'modified', 'seen')
Another small note is do you really want to create:
id = models.IntegerField(primary_key=True)
in your custom User model? By default an User model already has a field called id and it is the PK.
Related
I am creating rest APIs for a website in which users can purchase one of the provided subscriptions.
In this website there is a user-info API which returns the information about the logged in user which can be used to show their info on the website.
The problem is that, the mentioned API's serializer is a modelSerializer on the "User" model and the information that I want to return is the instance of "Subscription" model which the latest instance of "SubPurchase" model refers to.
These are my serializers, models and views.And I need to somehow return the user's current subscription's ID and name along with the user's information. If you have any further questions, ask me in the comments and I'll answer them.
# models.py
class User(AbstractBaseUser, PermissionsMixin):
userID = models.AutoField(primary_key=True)
username = models.CharField(max_length=100, unique=True, validators=[RegexValidator(regex="^(?=[a-z0-9._]{5,20}$)(?!.*[_.]{2})[^_.].*[^_.]$")])
email= models.EmailField(max_length=100, unique=True, validators=[EmailValidator()])
name = models.CharField(max_length=100)
isSuspended = models.BooleanField(default=False)
isAdmin = models.BooleanField(default=False)
emailActivation = models.BooleanField(default=False)
balance = models.IntegerField(default=0)
objects = UserManager()
USERNAME_FIELD = 'username'
class Subscription(models.Model):
subID = models.AutoField(primary_key=True)
nameOf = models.CharField(max_length=50)
price = models.PositiveIntegerField()
salePercentage = models.PositiveIntegerField(default=0)
saleExpiration = models.DateTimeField(default=datetime.datetime.now, blank=True)
def __str__(self):
return f"{self.nameOf}"
class SubPurchase(models.Model):
price = models.PositiveIntegerField()
dateOf = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
subscription = models.ForeignKey(Subscription, null=True, on_delete=models.SET_NULL)
def __str__(self):
return self.subscription
# serializers.py
class UserInfoSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
fields = ('userID', 'username','email', 'name', 'balance', 'emailActivation', 'isSuspended')
read_only_fields = ('userID', 'username','email', 'name', 'balance', 'emailActivation', 'isSuspended')
# views.py
class UserInfoViewSet(viewsets.ModelViewSet):
queryset = get_user_model().objects.all()
serializer_class = UserInfoSerializer
def get_queryset(self):
uID = getattr(self.request.user,'userID')
return get_user_model().objects.filter(userID=uID)
def get_object(self):
uID = getattr(self.request.user,'userID')
return self.queryset.filter(userID=uID)
Again, I need to change the UserInfoSerializer in a way that would give me the user's current subscription's name, ID and expiration date which would be 30 days after the purchase date
If you are only interested in the returned data, you can override the function to_representation of your serializer and create a serializer for your related model. If I understood correctly, the current subscription of your user is the last one (if sorted by "dateOf"). So something like that could do the trick
class SubscriptionSerializer(serializers.ModelSerializer):
class Meta:
model = Subscription
fields = ('nameOf', 'id', 'saleExpiration ')
class UserInfoSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
fields = ('userID', 'username','email', 'name', 'balance', 'emailActivation', 'isSuspended')
read_only_fields = ('userID', 'username','email', 'name', 'balance', 'emailActivation', 'isSuspended')
def to_representation(self, instance):
data = super().to_representation(instance)
current_subs = instance.subpurchase_set.order_by('dateOf').last().subscription
data['current_subscription'] = SubscriptionSerializer(instance=current_subs).data
return data
you can use NestedSerializers to achieve what you are looking for
basically, nested serialization is a method in which you can return, create, put..., into a model from another model, it goes like this..
models.py
class User(AbstractBaseUser, PermissionsMixin):
....
#user model data
class SubPurchase(models.Model):
...
user = models.ForeignKey(User, null=True, blank=True, on_delete=models.CASCADE)
serializers.py
class SubscriptionSerializer(serializers.ModelSerializer):
class Meta:
model = Subscription
fields =["anyfield you wanna include"]
class SubPurchaseSerializer(serializers.ModelSerializer):
class Meta:
model = SubPurchase
fields =["anyfield you wanna include"]
class UserInfoSerializer(serializers.ModelSerializer):
subpurchace = SubPurchaseSerializer()
subscription= SubscriptionSerializer() #later included in the fields of this serializer
class Meta:
model = get_user_model()
fields = ('userID','subpurchace', 'subscription', 'username','email', 'name', 'balance', 'emailActivation', 'isSuspended')
read_only_fields = ('userID', 'username','email', 'name', 'balance', 'emailActivation', 'isSuspended')
I have two models which have one to many relationship and I want to serialize the related fields on both ends.
The models:
class Mechanic(models.Model):
name = models.CharField('Name', max_length=256)
status = models.CharField('Status', choices=constants.MECHANIC_STATUS, default=constants.MECHANIC_STATUS[0][0],
max_length=64)
current_lat = models.CharField('Current Latitude', max_length=64)
current_lng = models.CharField('Current Longitude', max_length=64)
def __str__(self):
return self.name
class Service(models.Model):
type = models.CharField('Service Type', choices=constants.SERVICE_TYPES,
default=constants.SERVICE_TYPES[0][0], max_length=64)
mechanic = models.ForeignKey(Mechanic, on_delete=models.CASCADE, related_name='services')
vehicle_type = models.CharField('Vehicle Type', choices=constants.VEHICLE_TYPES,
default=constants.VEHICLE_TYPES[0][0], max_length=64)
charges = models.IntegerField('Charges')
def __str__(self):
return "{}, {}".format(self.mechanic, self.type)
The serializers:
class ServiceSerializer(serializers.ModelSerializer):
mechanic = MechanicSerializer(read_only=True) # Throws an error
class Meta:
model = Service
fields = ('id', 'type', 'mechanic', 'vehicle_type', 'charges')
class MechanicSerializer(serializers.ModelSerializer):
services = ServiceSerializer(many=True, read_only=True)
class Meta:
model = Mechanic
fields = ('id', 'name', 'status', 'services', 'current_lat', 'current_lng')
read_only_fields = ('id',)
How do I approach this problem? I understand that I've created a cyclic dependency because both serializers depend on each other.
Is there a better way to do it?
as an extenstion to my comment in the OP, create a new serializer class of Mechanic model before the ServiceSerializer class
class MechanicShortSerializer(serializers.ModelSerializer):
class Meta:
model = Mechanic
fields = '__all__'
class ServiceSerializer(serializers.ModelSerializer):
mechanic = MechanicShortSerializer(read_only=True) # replace with new serializer
class Meta:
model = Service
fields = ('id', 'type', 'mechanic', 'vehicle_type', 'charges')
class MechanicSerializer(serializers.ModelSerializer):
services = ServiceSerializer(many=True, read_only=True)
class Meta:
model = Mechanic
fields = ('id', 'name', 'status', 'services', 'current_lat', 'current_lng')
read_only_fields = ('id',)
I have two models one BookModel and another AuthorModel. I basically want to create a foreign key field to BookModel for referencing it to the AuthorModel. And I have been able to establish this successfully.
But when I retrieve an object/record of the BookModel I get the author id instead of the whole author object. How do I do that?
Here's what I've in my code
In models.py:
class AuthorModel(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField('Name', max_length=255)
pen_name = models.CharField('Pen Name', max_length=255)
class BookModel(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField('Book Name', max_length=255)
author = models.ForeignKey(Author, on_delete=models.CASCADE, blank=True, default='', related_name='book_authorid')
In serializers.py:
class AuthorSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = ('id', 'name', 'pen_name')
class BookSerializer(serializers.ModelSerializer):
author = AuthorSerializer()
class Meta:
model = Book
fields = ('id', 'name', 'author')
The view:
class BookViewSet(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
viewsets.GenericViewSet):
queryset = BookModel.objects.all()
serializer_class = BookSerializer
permission_classes = (AllowAny,)
The depth argument should give you the whole author object.
The default ModelSerializer uses primary keys for relationships, but you can also easily generate nested representations using the depth option
The depth option should be set to an integer value that indicates the
depth of relationships that should be traversed before reverting to a
flat representation.
class BookSerializer(serializers.ModelSerializer):
author = AuthorSerializer()
class Meta:
model = Book
depth = 1
fields = ('id', 'name', 'author')
I am building an API in Django using REST Framework but am running into an issue.
Serializers:
class SquadSerializer(serializers.Serializer):
class Meta:
model = Squad
fields = ('name')
id = serializers.IntegerField(read_only=True)
name = serializers.CharField(style={'base_template': 'textarea.html'})
class MembershipSerializer(serializers.Serializer):
class Meta:
model = Membership
fields = ('employee_id', 'squad_id')
squad = SquadSerializer()
employee = EmployeeSerializer()
class EmployeeSerializer(serializers.HyperlinkedModelSerializer):
habitat = HabitatSerializer()
class Meta:
model = Employee
fields = ('id', 'first_name', 'last_name', 'function',
'start_date', 'end_date', 'visible_site', 'habitat')
Models:
class Employee(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
function = models.CharField(max_length=50)
start_date = models.DateField()
end_date = models.DateField(null=True, blank=True)
visible_site = models.BooleanField()
habitat = models.ForeignKey(Habitat, on_delete=models.SET_NULL, null=True, blank=True)
class Squad(models.Model):
name = models.TextField(max_length=40)
class Membership(models.Model):
class Meta:
unique_together = (('employee', 'squad'))
employee = models.ForeignKey(Employee, on_delete=models.CASCADE, null=False, blank=True, default=1)
squad = models.ForeignKey(Squad, on_delete=models.CASCADE, null=False, blank=True, default=1)
The problem is that I keep running into this error:
AttributeError: Got AttributeError when attempting to get a value for field `name` on serializer `SquadSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `Membership` instance.
Original exception text was: 'Membership' object has no attribute 'name'.
When executing this test (and a couple others)
def test_membership_serializer_id_name_field_content(self):
"""
The name field of a squad should contain an id
"""
serializer = create_membership_serializer(self.membership, '')
self.assertEqual(serializer.data['id'], self.membership.id)
I've seen multipe people with the same issues here on Stack Overflow but the often suggest solution (to add many=True to SquadSerializer() and EmployeeSerializer()) doesn't work. I hope anyone here has any knowledge on why this happens.
If you want to map your seriailizer to your model, you should use ModelSerializer. In tupple, if it has only one value, you should write it as (1,) not (1). Your SquadSerializer should be like
class SquadSerializer(serializers.ModelSerializer):
class Meta:
model = Squad
fields = ('name',) # or ('id', 'name')
id = serializers.IntegerField(read_only=True)
name = serializers.CharField(style={'base_template': 'textarea.html'})
Your MembershipSerializer should be like
class MembershipSerializer(serializers.ModelSerializer):
class Meta:
model = Membership
fields = ('employee', 'squad')
squad = SquadSerializer()
employee = EmployeeSerializer()
How can I get a default value for null ForeignKey in Django. For ex:
class Owner(models.Model)
name = models.CharField(max_length=200, unique=True)
class Entity(models.Model):
name = models.CharField(max_length=200, unique=True)
owner = models.ForeignKey(Owner)
I am using DRF, so I tried this serializer:
class EntitySerializer(serializers.ModelSerializer):
owner = serializers.SerializerMethodField(read_only=True)
def get_owner(self, obj):
if obj.owner:
return obj.owner_id
return -1
class Meta:
model = Entity
fields = ('id', 'name', 'owner')
This works if I change the key owner to owner_id or something else. But I cannot do it as it has dependencies elsewhere.