I have been spending quite a lot of time trying to understand this error I got.
From models.py
class Company(models.Model):
name = models.CharField(max_length=100, unique=True, default="")
email = models.CharField(max_length=100, unique=True, default="")
password = models.CharField(max_length=100, unique=True, default="")
bsb = models.IntegerField(default=0)
account = models.IntegerField(default=0)
sign_up_date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class Payment(models.Model):
company = models.ForeignKey(Company, on_delete=models.CASCADE, related_name="payments")
name = models.CharField(max_length=100, unique=True, default="")
bsb = models.IntegerField(default=0)
account = models.IntegerField(default=0)
created_date = models.DateTimeField(auto_now_add=True)
paid_date = models.DateTimeField(default=datetime.now() + timedelta(days=36500))
status = models.IntegerField(default=0)
and from serializers.py
from rest_framework import serializers
from .models import Payment, Company
class PaymentSerializer(serializers.ModelSerializer):
company = serializers.ReadOnlyField(source='company.name')
class Meta:
model = Payment
fields = ['company', 'name', 'bsb', 'account', 'created_date', 'paid_date', 'status']
class CompanySerializer(serializers.ModelSerializer):
payments = serializers.PrimaryKeyRelatedField(many=True, queryset=Payment.objects.all())
class Meta:
model = Company
fields = ["name", "email", "password", "payments",
"bsb", "account", "sign_up_date"]
As you can see, I have included the related_name as "payments" for company attribute in Payment class. But when I go to http://localhost:8000/bill_payer/resources/company I got the following error:
AttributeError: 'User' object has no attribute 'payments'
I have verified that my Company class do indeed have the payments class through manage.py shell.
Any idea? I am new. Here's views.py in case it is important:
class PaymentList(APIView):
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def get(self, request):
payments = Payment.objects.all()
serializer = PaymentSerializer(payments, many=True)
return Response(serializer.data)
def perform_create(self, serializer):
serializer.save(company=self.request.user)
class PaymentDetail(APIView):
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def get_object(self, pk):
try:
return Payment.objects.get(pk=pk)
except Payment.DoesNotExist:
raise Http404
def get(self, request, pk):
payment = self.get_object(pk)
serializer = PaymentSerializer(payment)
return Response(serializer.data)
def put(self, request, pk):
payment = self.get_object(pk)
serializer = PaymentSerializer(payment, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk):
payment = self.get_object(pk)
payment.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
class CompanyList(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = CompanySerializer
class CompanyDetail(generics.RetrieveAPIView):
queryset = User.objects.all()
serializer_class = CompanySerializer
You get the error because you try to use User objects for CompanySerializer which expects Company model objects. It will work if you fix it as below.
class CompanyList(generics.ListAPIView):
queryset = Company.objects.all()
serializer_class = CompanySerializer
class CompanyDetail(generics.RetrieveAPIView):
queryset = Company.objects.all()
serializer_class = CompanySerializer
Please change this
class CompanyList(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = CompanySerializer
class CompanyDetail(generics.RetrieveAPIView):
queryset = User.objects.all()
serializer_class = CompanySerializer
to
class CompanyList(generics.ListAPIView):
queryset = Company.objects.all()
serializer_class = CompanySerializer
class CompanyDetail(generics.RetrieveAPIView):
queryset = Company.objects.all()
serializer_class = CompanySerializer
also your doing wrong here in perform_create method.
class PaymentList(APIView):
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def get(self, request):
payments = Payment.objects.all()
serializer = PaymentSerializer(payments, many=True)
return Response(serializer.data)
def perform_create(self, serializer):
serializer.save(company=self.request.user)
it should not be
serializer.save(company=self.request.user)
coz its not a company object. self.request.user is a user object. And you are trying to save user object to company object. its wrong.
try using
serializer.save()
or
serializer.save(company=serializer.data.get("company"))
Related
I'm working on a DRF project to learn about ContentType models.
I created a post model and comment model(ContentType) and then added comments to the post. Everything was working fine until I added django-debug-tool and duplicated queries.
I have the following questions:
I've defined a method(children) and property(total_replies) on the comment model. Since total_replies just calling children method and count the size of queryset. Will it result in hitting the database two or more times in case I use the children method in some other methods or property?
If the database is hitting multiple times, what solution two improve performance?
After adding select_related the num of queries has been reduced drastically.
Before using select_related
After using select_related
Is it good to use select_related at all places where Foreignkey has been used?
Blog app
models.py
class Post(models.Model):
title = models.CharField(verbose_name=_("Post Title"), max_length=50)
content = models.TextField()
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='blog_posts')
category = models.ForeignKey(Category, on_delete=models.CASCADE)
def __str__(self):
return self.title
#property
def comments(self):
instance = self
#qs = Comment.objects.filter_by_instance(instance) #before
qs = Comment.objects.select_related('user').filter_by_instance(instance)
return qs
#property
def get_content_type(self):
instance = self
content_type = ContentType.objects.get_for_model(instance.__class__)
return content_type
serializers.py
class PostSerializer(serializers.ModelSerializer):
author = UserPublicSerializer(read_only=True)
status_description = serializers.ReadOnlyField(source='get_status_display')
class Meta:
model = Post
fields = (
'url', 'id', 'title', 'author',
'content', 'category', 'total_likes',
)
class PostDetailSerializer(serializers.ModelSerializer):
author = UserPublicSerializer(read_only=True)
status_description = serializers.ReadOnlyField(source='get_status_display')
comments = serializers.SerializerMethodField()
class Meta:
model = Post
fields = (
'url', 'id', 'title', 'author', 'content',
'category', 'comments', 'total_likes'
)
def get_comments(self, obj):
request = self.context.get('request')
comments_qs = Comment.objects.filter_by_instance(obj)
comments = CommentSerializer(comments_qs, many=True, context={'request':request}).data
return comments
class PostListCreateAPIView(generics.ListCreateAPIView):
serializer_class = serializers.PostSerializer
# queryset = Post.objects.all().order_by('-id') # before
queryset = Post.objects.select_related('author').order_by('-id')
name = 'post-list'
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def perform_create(self, serializer):
serializer.save(author=self.request.user)
class PostRetrieveUpdateDestroyAPIView(generics.RetrieveUpdateDestroyAPIView):
serializer_class = serializers.PostDetailSerializer
# queryset = Post.objects.all().order_by('-id') # before
queryset = Post.objects.select_related('author').order_by('-id')
name = 'post-detail'
permission_classes = [permissions.IsAuthenticatedOrReadOnly, account_permissions.IsStaffOrAuthorOrReadOnly]
def perform_update(self, serializer):
serializer.save(author=self.request.user)
Comment app
models.py
class CommentManager(models.Manager):
def all(self):
qs = super().filter(parent=None)
return qs
def filter_by_instance(self, instance):
content_type = ContentType.objects.get_for_model(instance.__class__)
object_id = instance.id
qs = super().filter(content_type=content_type, object_id=object_id).select_related('user').filter(parent=None)
return qs
class Comment(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='comments')
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey(ct_field='content_type', fk_field='object_id')
parent = models.ForeignKey('self', null=True, blank=True, on_delete=models.CASCADE)
content = models.TextField()
objects = CommentManager()
def __str__(self):
if self.is_parent:
return f"comment {self.id} by {self.user}"
return f"reply {self.id} to comment {self.parent.id} by {self.user}"
def children(self):
return Comment.objects.select_related('user').filter(parent=self)
#property
def is_parent(self):
if self.parent is not None:
return False
return True
#property
def total_replies(self):
return self.children().count()
serializers.py
class CommentSerializer(serializers.ModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='comment-detail', lookup_field='pk')
user = UserPublicSerializer(read_only=True)
class Meta:
model = Comment
fields = ('url', 'user', 'id', 'content_type', 'object_id', 'parent', 'content', 'total_replies',)
class CommentChildSerializer(serializers.ModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='comment-detail', lookup_field='pk')
user = UserPublicSerializer(read_only=True)
class Meta:
model = Comment
fields = ('url', 'user', 'id', 'content',)
class CommentDetailSerializer(serializers.ModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='comment-detail', lookup_field='pk')
replies = serializers.SerializerMethodField()
class Meta:
model = Comment
fields = ('url', 'id', 'content_type', 'object_id', 'content', 'replies', 'total_replies',)
def get_replies(self, obj):
request = self.context.get('request')
if obj.is_parent:
return CommentChildSerializer(obj.children(), many=True, context={'request':request}).data
return None
views.py
class CommentListAPIView(generics.ListCreateAPIView):
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
queryset = Comment.objects.select_related('user').order_by('-id')
name = 'comment-list'
serializer_class = serializers.CommentSerializer
def perform_create(self, serializer):
serializer.save(user=self.request.user)
class CommentDetailAPIView(generics.RetrieveUpdateDestroyAPIView):
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
queryset = Comment.objects.select_related('user').all()
name = 'comment-detail'
serializer_class = serializers.CommentDetailSerializer
def perform_create(self, serializer):
serializer.save(user=self.request.user)
Thanks in advance.
That's exactly what django docs says about select_related :
"Returns a QuerySet that will “follow” foreign-key relationships, selecting additional related-object data when it executes its query. This is a performance booster which results in a single more complex query but means later use of foreign-key relationships won’t require database queries."
They describe select_related as something complex but good in term of transactional db cost.
I am new to Django Rest Framework. I have been using regular Django I want something similar to Django's CreateView for this model.
Intro: The superuser is the only user. All patients belong to the user and Embryos belong to the patient. One patient can have multiple embryos. Below are my models
class Patients(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
first_name = models.CharField(max_length=25)
last_name = models.CharField(max_length=35)
email = models.EmailField(unique=True)
class Embryo(models.Model):
patient = models.ForeignKey(Patients, on_delete=models.CASCADE)
code_name = models.CharField(max_length=100)
karyotype = models.CharField(max_length=100)
GENDER_CHOICES = (
("M", "Male"),
("F", "Female"),
)
sex = models.CharField(max_length=1, choices=GENDER_CHOICES)
I am trying to make create views for my models.
Below is what I have so far
class PatientsApiView(viewsets.ModelViewSet):
"""Handles Creating, reading and updating Patients"""
serializer_class = serializers.PatientsSerializer
queryset = Patients.objects.all()
authentication_classes = (TokenAuthentication,)
filter_backends = (filters.SearchFilter,)
search_fields = ("first_name", "last_name", "phone", "email",)
def form_valid(self, form, *args, **kwargs):
self.object = form.save(commit=False)
self.object.user = self.request.user
self.object.save()
return super().form_valid(form)
"""How can I get the above with Django Rest Framework. Can I do:
def __getattr__(self, user):
return self.request.user
"""
Below is the viewset for EmbroApiView
class EmbroApiView(viewsets.ModelViewSet):
"""Handles Creating, reading and updating Patients"""
serializer_class = serializers.EmbryoSerializer
queryset = Embryo.objects.all()
authentication_classes = (TokenAuthentication,)
filter_backends = (filters.SearchFilter,)
search_fields = ("code_name", "karyotype", "sex", "down_syndrome",)
def form_valid(self, form, *args, **kwargs):
self.object = form.save(commit=False)
pk = self.kwargs.get('pk')
self.object.patient = get_object_or_404(Patient, pk=pk)
self.object.save()
return super().form_valid(form)
"""How can I get the above with Django Rest Framework. Can I do:
def __getattr__(self, patient):
return get_object_or_404(Patients, pk=self.patient.pk)
"""
According to #Daniel Roseman solution. Have I done this correctly?
class PatientsApiView(viewsets.ModelViewSet):
"""Handles Creating, reading and updating Patients"""
serializer_class = serializers.PatientsSerializer
queryset = Patients.objects.all()
authentication_classes = (TokenAuthentication,)
filter_backends = (filters.SearchFilter,)
permission_classes = (IsAuthenticated,)
search_fields = ("first_name", "last_name", "phone", "email",)
def perform_create(self, serializer):
serializer.save(user=self.request.user)
class EmbroApiView(viewsets.ModelViewSet):
"""Handles Creating, reading and updating Patients"""
serializer_class = serializers.EmbryoSerializer
queryset = Embryo.objects.all()
authentication_classes = (TokenAuthentication,)
filter_backends = (filters.SearchFilter,)
permission_classes = (IsAuthenticated,)
search_fields = ("code_name", "karyotype", "sex", "down_syndrome",)
def perform_create(self, serializer):
serializer.save(patient_id=self.kwargs['pk'])
I'm not sure why you mention __getattr__ in the comment, that would have nothing to do with this.
The hooks for extra actions on create and update are called perform_create and perform_update. So as the docs show, you can do:
def perform_create(self, serializer)
serializer.save(user=self.request.user)
etc.
I have a below view, serializer and model. Using BrowsableAPIRenderer i render HTML page. I need to assign current user id to user_id field and hide the same from HTML rendering(not allow to edit and enter). I am using Django rest framework 2.3.13. Kindly help me to achieve this.
serializer.py
class TagOwnerSerializer(serializers.HyperlinkedModelSerializer):
tag_id = serializers.SlugRelatedField(slug_field='tag_id')
user_id = serializers.IntegerField(write_only=True)
class Meta:
model = TagOwner
fields = ('url','tag_id','start_time','end_time','user_id')
views.py
class TagOwnerViewSet(viewsets.ModelViewSet):
"""
TagOwner table view set.
"""
model = TagOwner
queryset = TagOwner.objects.all()
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
serializer_class = TagOwnerSerializer
renderer_classes = (BrowsableAPIRenderer, JSONRenderer,JSONPRenderer,XMLRenderer,YAMLRenderer)
filter_backends = (filters.DjangoFilterBackend, filters.SearchFilter ,filters.OrderingFilter)
filter_class = TagOwnerFilter
#search_fields = ('tag_id',)
ordering_fields = '__all__'
def get_queryset(self):
user = self.request.user
if self.request.user.is_authenticated():
if not user:
return []
return TagOwner.objects.filter(user_id=user.id)
public_tags = TagReads.objects.filter(public=True).values_list('tag_id').distinct()
return TagOwner.objects.filter(tag_id__in=public_tags)
def create(self,request):
serializer = self.serializer_class(data=request.DATA)
if serializer.is_valid():
user=self.request.user.id
serializer.save(user_id = user)
return Response(serializer.data, status = status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
models.py
class TagOwner(models.Model):
user_id = models.IntegerField(blank=False,db_column='user_id',)
tag_id = models.OneToOneField('Tags',on_delete=models.CASCADE,primary_key=True,db_column='tag_id',)
start_time = models.DateTimeField(blank=True, null=True)
end_time = models.DateTimeField(blank=True, null=True)
class Meta:
managed = False
db_table = 'tag_owner'
Found it myself.
serializer.py
class TagOwnerSerializer(serializers.HyperlinkedModelSerializer):
tag_id = serializers.SlugRelatedField(slug_field='tag_id')
user_id = serializers.Field(source='user_id')
#user_id = serializers.PrimaryKeyRelatedField(read_only=True,default=serializers.CurrentUserDefault())
class Meta:
model = TagOwner
fields = ('url','tag_id','start_time','end_time','user_id')
views.py
class TagOwnerViewSet(viewsets.ModelViewSet):
"""
TagOwner table view set.
"""
model = TagOwner
queryset = TagOwner.objects.all()
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
serializer_class = TagOwnerSerializer
renderer_classes = (BrowsableAPIRenderer, JSONRenderer,JSONPRenderer,XMLRenderer,YAMLRenderer)
filter_backends = (filters.DjangoFilterBackend, filters.SearchFilter ,filters.OrderingFilter)
filter_class = TagOwnerFilter
#search_fields = ('tag_id',)
ordering_fields = '__all__'
def get_queryset(self):
user = self.request.user
if self.request.user.is_authenticated():
if not user:
return []
return TagOwner.objects.filter(user_id=user.id)
public_tags = TagReads.objects.filter(public=True).values_list('tag_id').distinct()
return TagOwner.objects.filter(tag_id__in=public_tags)
def pre_save(self, obj):
obj.user_id = self.request.user.id
This worked perfectly for me.
I am getting not null contraint failed error while posting a group. How should i fix it? I don't want to show the user in the api so i have not used it in the serializer fields. Do i have to compulsorily add it there?
Here is my model, serializer and APIView
class DeviceGroup(models.Model):
token = models.UUIDField(default=uuid.uuid4, unique=True, editable=False)
name = models.CharField(max_length=250, blank=False, null=False)
owner = models.ForeignKey(User, blank=False, null=False)
class DeviceGroupSerializer(serializers.ModelSerializer):
id = serializers.UUIDField(source='token', format='hex', read_only=True)
class Meta:
model = DeviceGroup
fields = ['id','name']
class DevicesGroupsAPIView(APIView):
permission_classes = (permissions.IsAuthenticated,)
serializer_class = DeviceGroupSerializer
def get_object(self, user, token):
try:
return BaseDevice.objects.filter(owner=user).get(token=token)
except ObjectDoesNotExist:
return error.RequestedResourceNotFound().as_response()
def get(self, request, format=None):
"""
Returns a list of groups
"""
reply = {}
try:
groups = DeviceGroup.objects.filter(owner=request.user)
reply['data'] = DeviceGroupSerializer(groups, many=True).data
except:
reply['data'] = []
return Response(reply, status.HTTP_200_OK)
def post(self, request, format=None):
"""
create a new group
"""
print('request.data', request.data)
print('user', request.user)
serializer = DeviceGroupSerializer(data=request.data)
print('serializer', serializer)
if serializer.is_valid():
serializer.save()
return Response(serializers.data, status.HTTP_200_OK)
return Response(serializer.errors, status.HTTP_204_NO_CONTENT)
see this carefully, user is not in request.data:
serializer = DeviceGroupSerializer(data={
'name':request.data['name'],
'owner':request.user.id,
})
also check that the serializer allows the owner to be used
from django.contrib.auth.models import User
class DeviceGroupSerializer(serializers.ModelSerializer):
id = serializers.UUIDField(source='token', format='hex', read_only=True)
owner = serializers.PrimaryKeyRelatedField(queryset=User.objects.all())
class Meta:
model = DeviceGroup
fields = ['id','name', 'owner']
I'm building a REST web service, the GET method seems to be working all right
but when it comes to POST error message always shows up:
'dict' object has no attribute 'save'
models.py
from django.db import models
class Users(models.Model):
Fullname = models.CharField(max_length=50)
Username = models.CharField(max_length=15)
Password = models.CharField(max_length=8)
Email = models.CharField(max_length=50, unique=True)
Type = models.CharField(max_length=5)
TwitterName = models.CharField(max_length=15, unique=True)
FacebookName = models.CharField(max_length=15, unique=True)
CreationDate = models.DateTimeField()
serializer.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = Users
fields = ('Fullname', 'Username', 'Email', 'Password', 'Type', 'TwitterName', 'FacebookName')
views.py
#api_view(['GET', 'POST'])
def users_list(request, format=None):
if request.method == 'GET':
users = Users.objects.all()
serializer = UserSerializer(users, many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = UserSerializer(request.DATA, many=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.data, status=status.HTTP_400_BAD_REQUEST)
I'm using PyCharm and Django 1.7
If you want to save the POST data then you should pass the data to data keyword argument:
serializer = UserSerializer(data=request.DATA, many=True)
if serializer.is_valid():
...
Also I would suggest you to use Class based views with Mixins as that will make your code much cleaner and shorter:
from rest_framework import generics, mixins
class UserList(mixins.ListModelMixin,
mixins.CreateModelMixin,
generics.GenericAPIView):
queryset = Users.objects.all()
serializer_class = UserSerializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)