how to fix AttributeError: 'RelatedManager' object has no attribute 'id' - django

I have two nested models (task and Proposal) with a foreign key relationship, i've followed every necessary step but i'm getting an inegrity error
below is d err and codes
class JobPost(models.Model):
id= models.UUIDField(default=uuid.uuid4, primary_key=True,
editable=False)
user = models.ForeignKey(CustomUser, related_name='users',
on_delete=models.CASCADE)
title = models.CharField(max_length=255)
class Proposal(models.Model):
user = models.OneToOneField(CustomUser, related_name="user", on_delete=models.CASCADE)
id = models.UUIDField(default=uuid.uuid4, primary_key=True, editable=False)
task = models.ForeignKey(JobPost, on_delete=models.CASCADE, related_name='proposal')
Serializer
1st serializer
class JobSerializer(serializers.ModelSerializer):
user = serializers.CharField(source='user.username', read_only=True )
user_id = serializers.CharField(source='user.id', read_only=True)
proposals = ProposalSerializer(many=True, read_only=True)
class Meta:
model = JobPost
fields = [
'user',
'user_id',
'id',
'proposals',
'etc'
]
2nd Serializer
class ProposalSerializer(serializers.ModelSerializer):
user = serializers.CharField(source='user.username',read_only=True)
class Meta:
model = Proposal
fields = [
'id',
'proposal_description',
'duration',
'bid',
]
def create(self, validated_data, instance=None):
if 'task' in validated_data:
task = validated_data.pop('task')
else:
task = JobPost.objects.create(**validated_data)
user_proposal = Proposal.objects.update_or_create(
task = task, defaults=validated_data
)
return user_proposal
APIVIEW
class ProposalAPIView(generics.CreateAPIView):
serializer_class = ProposalSerializer
look_up = 'id',
queryset = Proposal.objects.all()
permissions_classes = [permissions.IsAuthenticated]
2nd APIView
class CreateJobPost(generics.CreateAPIView):
serializer_class = JobSerializer
permissions_classes = [permissions.IsAuthenticated]
err msg
task = CustomUser.objects.get(pk=self.request.user.task.id)
AttributeError: 'RelatedManager' object has no attribute 'id'
can anyone pls help

You haven't mentioned your models. Try this.
task_id = models.PositiveIntegerField(blank=True,null=True)

Related

Django DRF serializers, how to add new field, fusion of two models?

I have two models, I have to make an endpoint where the results of two tables should appear in the json, which have a fongeringkey that joins them.
My code is the following:
models.py
class Property(models.Model):
address = models.CharField(max_length=120)
city = models.CharField(max_length=32)
price = models.BigIntegerField()
description = models.TextField(blank=True, null=True)
year = models.IntegerField(blank=True, null=True)
class Meta:
managed = False
db_table = 'property'
class StatusHistory(models.Model):
property = models.ForeignKey(Property, on_delete=models.CASCADE)
status = models.ForeignKey(Status, on_delete=models.CASCADE)
update_date = models.DateTimeField()
class Meta:
managed = False
db_table = 'status_history'
views.py
class StandardResultsSetPagination(PageNumberPagination):
page_size = 10
page_size_query_param = "page_size"
max_page_size = 1000
class PropertyListView(viewsets.ModelViewSet):
http_method_names = ['get', 'head']
serializer_class = PropertyListSerializer
queryset = Property.objects.all()
pagination_class = StandardResultsSetPagination
def get_serializer_class(self):
if self.action == 'list':
return PropertyListSerializer
return PropertyListSerializer
def get_queryset(self):
queryset = Property.objects.all()
if self.request.GET.get('year'):
queryset = queryset.filter(year=self.request.GET.get('year'))
if self.request.GET.get('city'):
queryset = queryset.filter(city=self.request.GET.get('city'))
if self.request.GET.get('status'):
queryse = queryset.filter(statushistory__status=self.request.GET.get('status'))
else:
queryset = queryset.order_by('-year')
return queryset
def list(self, request):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
serializers.py
class PropertyListSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Property
fields = ('id', 'address', 'city', 'price', 'description', 'year')
class StatusHistoryListSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = StatusHistory
fields = ('property', 'status', 'update_date')
I can correctly filter the parameters, city, year(model Property) and above all status(model StatusHistory) with the following code snippet:
if self.request.GET.get('status'):
queryset = queryset.filter(statushistory__status=self.request.GET.get('status'))
My problem is how I show in my JSON Response the new field fusion of the two models statushistory__statustry adding the following in serializers.py
class PropertyListSerializer(serializers.HyperlinkedModelSerializer):
statushistory_set =StatusHistoryListSerializer(many=True)
class Meta:
model = Property
fields = ('id', 'address', 'city', 'price', 'description', 'year', 'statushistory_set')
class StatusHistoryListSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = StatusHistory
fields = ('property', 'status', 'update_date')
Without any result.
I think you could set related_name attribute in the foreign key field.
class StatusHistory(models.Model):
property = models.ForeignKey(Property, on_delete=models.CASCADE, related_name = 'status_histories')
status = models.ForeignKey(Status, on_delete=models.CASCADE)
...
And in the PropertyListSerializer,
class PropertyListSerializer(serializers.HyperlinkedModelSerializer):
status_histories = StatusHistoryListSerializer(many=True, read_only = True)
class Meta:
model = Property
fields = ('id', 'address', 'city', 'price', 'description', 'year', 'status_histories')
You can also get property field as an object in the StatusHistory like the following:
class StatusHistoryListSerializer(serializers.HyperlinkedModelSerializer):
property = PropertyListSerializer(read_only = True)
class Meta:
model = StatusHistory
fields = ('property', 'status', 'update_date')

Django - grabbing an additional field from related table

I have the following models:
class User(AbstractBaseUser, PermissionsMixin):
SUPERVISOR = 1
REVIEWER = 2
VERIFIER = 3
READ_ONLY = 4
USER_TYPE = [
(SUPERVISOR, 'Supervisor'),
(REVIEWER, 'Reviewer'),
(VERIFIER, 'Verifier'),
(READ_ONLY, 'Read Only'),
]
email = models.EmailField(max_length=50, unique=True)
name = models.CharField(max_length=100)
phone = models.CharField(max_length=50, null=True)
role = models.IntegerField(
choices=USER_TYPE,
default=READ_ONLY
)
is_active = models.BooleanField(default=True)
class Comment(models.Model):
text = models.TextField()
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.PROTECT
)
View:
class CommentViewSet(BaseCertViewSet):
queryset = Comment.objects.all()
serializer_class = serializers.CommentSerializer
Serializer:
class CommentSerializer(serializers.ModelSerializer):
user = serializers.SlugRelatedField(
read_only=True,
slug_field='name'
)
class Meta:
model = Comment
fields = ('id', 'text', 'user',)
read_only_fields = ('id',)
My question: when I hit the comment API endpoint, I'd like it to return the user role from the user model as well. How do I go about doing that?
I believe you can use a QuerySet.annotation:
EDIT: F is from django.db.models so you have to import that as well.
queryset = Comment.objects.annotate(user_role=F("user__role")) in your CommentViewSet
EDIT:
In order to get the serializer to recognize the field that you are trying to add to the QuerySet, you must also define the field on the serializer like this:
class CommentSerializer(serializers.ModelSerializer):
user = serializers.SlugRelatedField(
read_only=True,
slug_field='name'
)
# add line below to your code
user_role = IntegerField()
class Meta:
model = Comment
# you have to add it to the list of fields as well
fields = ('id', 'text', 'user', 'user_role')
read_only_fields = ('id',)
The solution that worked for me (not sure it's the most elegant one, happy to change to a better way of doing that):
class CommentSerializer(serializers.ModelSerializer):
"""Serializer for Comment object"""
user = serializers.SlugRelatedField(
read_only=True,
slug_field='name'
)
role = serializers.SerializerMethodField()
def get_role(self, obj):
user = obj.user_id
role = User.objects.only('id').get(
id=user).role
return role
class Meta:
model = Comment
fields = ('id', 'value', 'text', 'user', 'role',
'date_created', 'date_updated')
read_only_fields = ('id',)

Cannot query "object": Must be "User" instance

I am creating sample-api which have posts and followers. Post should visible to followers only
My models.py
from django.contrib.auth.models import User
class Post(models.Model):
creator = models.ForeignKey(User, related_name='creator_post_set', null=True, on_delete=models.CASCADE)
title = models.CharField(max_length=25)
created_date = models.DateTimeField(auto_now_add=True)
content = models.TextField()
likes = models.BigIntegerField(null=True)
comments = models.BigIntegerField(null=True)
class Follow(models.Model):
follower = models.ForeignKey(User, related_name='following', null=True, on_delete=models.CASCADE)
followed_on = models.DateTimeField(auto_now_add=True)
following = models.ForeignKey(User, related_name='follower',null=True, on_delete=models.CASCADE)
My serializers.py for the models:
class UserSerializer(ModelSerializer):
password = serializers.CharField(write_only=True)
def create(self, validated_data):
user = User.objects.create_user(
username=validated_data['username'],
password=validated_data['password'],
first_name=validated_data['first_name'],
last_name=validated_data['last_name'],
)
return user
class Meta:
model = User
fields = ('password', 'username', 'first_name', 'last_name',)
class PostListSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['title', 'content', 'created_date',]
class FollowSerializer(serializers.ModelSerializer):
class Meta:
model = Follow
fields = '__all__'
My views.py:
class PostList(generics.ListCreateAPIView):
serializer_class = PostListSerializer
follow_model = FollowSerializer.Meta.model
post_model = PostSerializer.Meta.model
def get_queryset(self):
try:
followers = self.follow_model.objects.get(follower_id =
self.request.user.id)
queryset = self.post_model.objects.get(creator__in = followers)
except self.follow_model.DoesNotExist:
queryset = None
return queryset
When I call this view it returns the following error:
Cannot query "Follow object (1)": Must be "User" instance.
I need help Thanks in Advance.
As I can see, Post model's creator is FKed to User model. So you need to query using User model instance, not Follower model.
You can use the following code:
following = self.request.user.following.all().values_list('follower', flat=True) # because of related name
queryset = self.post_model.objects.filter(creator_id__in = list(following))
Here I have first retrieved the user ids using self.request.following.all() by reverse relationship. Then I have extracted the user ids using values_list. After that, I have used it in Post.objects.filter(...) method.

adding to a manytomany field in django

I am trying to save a many to many tags field when creating a post.
this is my post request to create a new post
{
"name":"testpost1",
"caption":"test caption n",
"tags":[{"name":"tag1"}]
}
Models
class Tags(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True, editable=False)
name = models.CharField(max_length=50, unique=True)
created_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name="created_tags")
time_created = models.DateTimeField(auto_now_add=True)
followers = models.ManyToManyField(User, related_name="following_tags")
posts = models.ManyToManyField('Posts', related_name='tags', symmetrical=True)
class Posts(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True, editable=False)
name = models.CharField(max_length=50)
caption = models.TextField(max_length=1000)
created_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
The serializer for Post and tag
class TagsSerializerMini(serializers.ModelSerializer):
class Meta:
model = Tags
fields = ('name', )
class PostsSerializer(QueryFieldsMixin, serializers.ModelSerializer):
created_by = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())
tags = TagsSerializerMini(many=True, required=False)
class Meta:
model = Posts
fields = ('id', 'name', 'caption', 'tags', 'num_reactions', 'num_comments', 'created_by', 'posted_time',
'updated_time', )
read_only_fields = ('id', 'posted_time', 'updated_time', )
def create(self, validated_data):
tags_data = validated_data.pop('tags', [])
post = Posts.objects.create(**validated_data)
for tag in tags_data:
t, _ = Tags.objects.get_or_create(name=tag["name"], created_by=self.context['request'].user)
t.posts.add(post)
return post
Now the issue is, when i am addding a post to tag t.posts.add(post) it is throwing an Error django.db.utils.IntegrityError: FOREIGN KEY constraint failed. I tried adding a post from the shell also, its giving the same error.
I got similar problem and I solved it by using an intermediary model (using through), here is how should look your code:
models.py:
class Tags(models.Model):
# ...
posts = models.ManyToManyField('Posts', related_name='tags', symmetrical=True, through='PostsTag')
class PostTag(models.Model):
tag = models.ForeignKey(Tags)
post = models.ForeignKey(Posts)
serializer:
# ...
def create(self, validated_data):
tags_data = validated_data.pop('tags', [])
post = Posts.objects.create(**validated_data)
for tag in tags_data:
t, _ = Tags.objects.get_or_create(name=tag["name"], created_by=self.context['request'].user)
PostTag.objects.create(tag=t, post=post) # <----- Create m2m relation
return post
Note: I didn't tested the code, but hope this helps!

Django Restful: Nested Serializers

I'm adding a 'tests' field to my 'Sample' model, where 'tests' will be a list of 'TestRequest' objects. Currently, I'm getting this error:
Got AttributeError when attempting to get a value for field `tests` on serializer `SampleSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `Sample` instance.
Original exception text was: 'Sample' object has no attribute 'tests'.
'tests' is not a field on my model. I'm just trying to add it to the serialized data. Currently, I can get a nested serializer to work for 'klass' but that is because it's defined in the model.
Models:
class Sample(models.Model):
name = models.CharField(max_length=50, null=False, blank=False)
comments = models.TextField(null=True, blank=True)
klass = models.ForeignKey('samples.Batch', null=True, blank=True,
related_name='samples', verbose_name='Batch')
product = models.ForeignKey('customers.Product', blank=False)
NOTRECEIVED = 'nr'
RECEIVED = 'rc'
DISCARDED = 'dc'
DEPLETED = 'dp'
SAMPLE_STATUS = (
(NOTRECEIVED, 'not received'),
(RECEIVED, 'received'),
(DISCARDED, 'discarded'),
(DEPLETED, 'depleted'),
)
status = models.CharField(
max_length=2, choices=SAMPLE_STATUS, default=NOTRECEIVED)
is_recycling = models.BooleanField(default=False)
is_submitted = models.BooleanField(default=False)
received_date = models.DateTimeField(
_('date received'), null=True, blank=True)
class TestRequest(models.Model):
client = models.ForeignKey('customers.Client')
company = models.ForeignKey('customers.Company')
sample = models.ForeignKey('samples.Sample')
procedure_version = models.ForeignKey('registery.ProcedureVersion')
replicates = models.PositiveIntegerField(editable=True, null=True, blank=True)
created_date = models.DateTimeField('Date created', auto_now_add=True)
last_updated = models.DateTimeField(auto_now=True)
comments = models.TextField('Comments', blank=True)
Serializers:
class TestSerializer(serializers.ModelSerializer):
href = serializers.HyperlinkedIdentityField(lookup_field='pk', lookup_url_kwarg='pk', read_only=True, view_name='samples_api:test-detail')
class Meta:
model = TestRequest
fields = ('id', 'href',)
class SampleBatchSerializer(serializers.ModelSerializer):
href = serializers.HyperlinkedIdentityField(
lookup_field='pk', lookup_url_kwarg='batch_pk', read_only=True, view_name='samples_api:batch-detail')
class Meta:
model = Batch
fields = ('id', 'href',)
class SampleSerializer(serializers.ModelSerializer):
tests = TestSerializer(many=True)
klass = SampleBatchSerializer(many=False)
class Meta:
model = Sample
# list_serializer_class = FilteredListSerializer
fields = ('id', 'name', 'tests', 'klass',)
def create(self, validated_data):
...
def update(self, instance, validated_data):
...
Viewsets:
class TestRequestViewSet(viewsets.ModelViewSet):
"""
Viewset for the TestRequest model
"""
serializer_class = TestRequestSerializer
def get_queryset(self):
client = get_object_or_404(Client, user=self.request.user)
return TestRequest.objects.filter(company=client.company)
def perform_create(self, serializer):
# Override default creatation to provide request based information.
client = get_object_or_404(Client, user=self.request.user)
company = client.company
serializer.save(client=client, company=company)
class SampleViewSet(viewsets.ModelViewSet):
"""
Viewset for the Sample model
"""
serializer_class = SampleSerializer
def get_queryset(self):
client = get_object_or_404(Client, user=self.request.user)
return Sample.objects.filter(klass__company=client.company)
I would rather not have to add the field to the model. A 'Sample' can have many 'TestRequest's but a 'TestRequest' can only have one 'Sample'.
How do I get my serializer to add the 'tests' field that isn't in the model?
in your SampleSerializer. You have specified 'tests' which is not in your Sample class in your model...
Use nested SerializerMethodField as below....
tests = serializers.SerializerMethodField()
def get_tests(self, obj):
var=TestRequestSerializer(obj.id)
return var.data