django AttributeError when saving data with DRF - python

I have a problem when I make a POST.
the entry is created (I can see it in django Admin panel) but DRF is throwing me an error
AttributeError at /api/v1/reviews/
'Review' object has no attribute 'review_score'
review_score beeing a SerializerMethodField in serializer.py needed for the frontend (GET is working perfectly)
model.py
class Review(models.Model):
company = models.ForeignKey(Company, related_name='company_review', on_delete=models.CASCADE)
title = models.CharField(max_length=255)
comments = models.TextField()
validated = models.BooleanField(default=False)
note_quality = models.DecimalField(max_digits=5, decimal_places=2)
note_timing = models.DecimalField(max_digits=5, decimal_places=2)
note_qualprix = models.DecimalField(max_digits=5, decimal_places=2)
note_react = models.DecimalField(max_digits=5, decimal_places=2)
note_followup = models.DecimalField(max_digits=5, decimal_places=2)
note_clean = models.DecimalField(max_digits=5, decimal_places=2)
created_at = models.DateField(auto_now_add=True)
created_by = models.ForeignKey(User, related_name='created_review', on_delete=models.CASCADE)
def __str__(self):
return self.title
views.py
class ReviewViewSet(viewsets.ModelViewSet):
serializer_class = ReviewSerializer
queryset = Review.objects.all()
def get_queryset(self):
company_id = self.request.query_params.get('company_id', None)
return self.queryset.filter(company_id=company_id, validated=True).annotate(
review_score = Sum(F('note_quality')+F('note_timing')+F('note_qualprix')+F('note_react')+F('note_followup')+F('note_clean'))/6,
)
def perform_create(self, serializer):
obj = serializer.save(created_by=self.request.user)
serializer.py
class ReviewSerializer(serializers.ModelSerializer):
created_by = UserCreateSerializer(read_only=True)
review_score = serializers.SerializerMethodField()
def get_review_score(self, obj):
rounded = round_up(obj.review_score, 0.5)
return rounded
class Meta:
model = Review
read_only_fields = ('created_at'),
fields = ('id','company','title','comments','validated','note_quality','note_timing','note_qualprix','note_react','note_followup','note_clean','created_at', 'created_by', 'review_score')

review_score at ReviewSerializer should be a serializers.ReadOnlyField(). Change that and it should work.

You should remove row
obj.save()
in the perform_create function. Model saving is already happening in the line
obj = serializer.save(created_by=self.request.user)

Related

drf: serializers.ModelField > How can i get user_id appropriately?

I want to implement the 'user_id' to be automatically saved at the backend without receiving it from the client. (when create object!)
This is my code.
models.py
class User(AbstractUser):
username = None
email = models.EmailField(max_length=255, unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = UserManager()
social_profile = models.URLField(null=True,blank=True)
realname = models.CharField(max_length=50, blank=True)
nickname = models.CharField(max_length=50, null=True, unique=True)
address = models.CharField(max_length=200, blank=True)
phone = models.CharField(max_length=100, blank=True)
def __str__(self):
return self.email
class Item(models.Model):
user_id = models.ForeignKey(User, related_name='item_sets', on_delete=models.CASCADE)
category_id = models.ForeignKey(Category, related_name='item_sets', on_delete=models.DO_NOTHING)
description = models.TextField()
feature = models.TextField()
product_defect = models.TextField()
size = models.CharField(max_length=6)
height = models.DecimalField(max_digits=4, decimal_places=1, default=0)
weight = models.DecimalField(max_digits=4, decimal_places=1, default=0)
condition = models.CharField(max_length=20)
price = models.IntegerField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
sold_out = models.BooleanField(default=False)
def __str__(self):
return self.description
view.py
class ItemViewSet(ModelViewSet):
queryset = Item.objects.all()
serializer_class = ItemSerializer
filter_backends = [SearchFilter, OrderingFilter]
search_fields = ['description'] # ?search=
ordering_fields = ['created_at'] # ?ordering=
ordering = ['-created_at']
authentication_classes = (JWTCookieAuthentication,)
# create
def create(self, request, *args, **kwargs):
city = request.data['city']
gu = request.data['gu']
dong = request.data['dong']
if city is not None and gu is not None and dong is not None:
location = Location.objects.get(
Q(city=city) & Q(gu=gu) & Q(dong=dong)
)
else:
return Response(
{"message": "주소정보를 모두 입력해주세요."},
status=status.HTTP_400_BAD_REQUEST
)
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
new_item = Item.objects.get(id=serializer.data['id'])
headers = self.get_success_headers(serializer.data)
LocationSet.objects.create(item_id=new_item, location_id=location)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
# here!
def perform_create(self, serializer):
serializer.save(user_id=self.request.user)
serializers.py
class ItemSerializer(serializers.ModelSerializer):
photos = PhotoSerializer(source='photo_sets', many=True, read_only=True)
style_photos = StylePhotoSerializer(source='style_photo_sets', many=True, read_only=True)
user_id = serializers.ModelField(model_field=User()._meta.get_field('id'), required=False) #here!
class Meta:
model = Item
fields = '__all__'
# Photo, StylePhoto
def create(self, validated_data):
images_data = self.context['request'].FILES
item = Item.objects.create(**validated_data)
for photo_data in images_data.getlist('photos_data'):
Photo.objects.create(item_id=item, photo=photo_data)
for style_photo_data in images_data.getlist('style_photos_data'):
StylePhoto.objects.create(item_id=item, user_id=self.context['request'].user,
photo=style_photo_data)
print(User()._meta.get_field('id'))
return item
so I overrided perform_create() in views.py
and write 'user_id = serializers.ModelField(model_field=User()._meta.get_field('id'), required=False)' in serializers.py
but it doesn't work well like my intend...
correct user_id is 8. but my object has 18..:<
18 is just item_id, not user_id..
what should I do to fix this error?
Try this:
views.py : use the perform_create() method to save the user_id.
Note: the user must be authenticated so that we can get them from the request.
class ItemViewSet(ModelViewSet):
# your code ...
# create
def create(self, request, *args, **kwargs):
# your code code ...
def perform_create(self, serializer): # new
serializer.save(user_id=self.request.user)
serializers.py :
Get the user_id from the database and make it readonly as it is not supposed to be edited by the user:
class ItemSerializer(serializers.ModelSerializer):
# your code ..
user_id = serializers.ReadOnlyField(source='user_id.id') # new
class Meta:
model = Item
fields = '__all__'
# Photo, StylePhoto
def create(self, validated_data):
images_data = self.context['request'].FILES
item = Item.objects.create(**validated_data)
for photo_data in images_data.getlist('photos_data'):
Photo.objects.create(item_id=item, photo=photo_data)
for style_photo_data in images_data.getlist('style_photos_data'):
StylePhoto.objects.create(item_id=item,photo=style_photo_data) # new
print(User()._meta.get_field('id'))
return item
I would also recommend you change the user_id field to something like owner to prevent future confusion.

Django clean method cause is_valid() false

here is my model:
class Product(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
name = models.CharField(max_length=200)
description = models.TextField(blank=True)
price = models.DecimalField(max_digits=10, decimal_places=2)
stock = models.PositiveIntegerField()
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
objects = models.Manager()
available = ProductStockManager()
and I have a form like this:
class ProductForm(ModelForm):
name = forms.CharField(max_length=200)
price = forms.DecimalField(decimal_places=2)
description = forms.Textarea()
class Meta:
model = Product
fields = ['category', 'name', 'description', 'price', 'stock']
def clean_description(self):
pass
def clean_price(self):
pass
def clean_name(self):
pass
in my form when I use clean_field() for each field it makes is_valid() False except clean_description() what is this for ?
why when I use clean_price(), clean_name() my is_valid() returns False?
and why it doesn't when I use clean_description() ?

How to nest these Serializes without facing AttributeError: 'BlogPost' object has no attribute 'review_set'

I followed Dennis Ivy proshop Tutorial He used the same approach as the code is
class ReviewSerializer(serializers.ModelSerializer):
class Meta:
model = Review
fields = '__all__'
class ProductSerializer(serializers.ModelSerializer):
reviews = serializers.SerializerMethodField(read_only=True)
class Meta:
model = Product
fields = '__all__'
def get_reviews(self, obj):
reviews = obj.review_set.all()
serializer = ReviewSerializer(reviews, many=True)
return serializer.data
Now I need a Blog for the eCommerce Project and I created another app named blog and Created the models as
class BlogPost(models.Model):
_id = models.AutoField(primary_key=True, editable=False)
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
title = models.CharField(max_length=200, null=True, blank=True, help_text="Like How To Treat Hypertension etc")
image = models.ImageField(null=True, blank=True,
default='/placeholder.png')
rating = models.DecimalField(
max_digits=7, decimal_places=2, null=True, blank=True)
numReviews = models.IntegerField(null=True, blank=True, default=0)
createdAt = models.DateTimeField(auto_now_add=True)
youtubeVideoLink = models.CharField(max_length=1000, null=True , blank=True)
def __str__(self):
return str(self.createdAt)
class BlogPostReview(models.Model):
blogpost = models.ForeignKey(BlogPost, on_delete=models.SET_NULL, null=True)
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
name = models.CharField(max_length=200, null=True, blank=True)
rating = models.IntegerField(null=True, blank=True, default=0)
comment = models.TextField(null=True, blank=True)
createdAt = models.DateTimeField(auto_now_add=True)
_id = models.AutoField(primary_key=True, editable=False)
def __str__(self):
return str(self.rating)
But when I serialize them via same approach as mentioned above....
class BlogPostReviewSerializer(serializers.ModelSerializer):
class Meta:
model = BlogPostReview
fields = '__all__'
class BlogPostSerializer(serializers.ModelSerializer):
blog_post_reviews = serializers.SerializerMethodField(read_only=True)
class Meta:
model = BlogPost
fields = '__all__'
def get_blog_post_reviews(self, obj):
blog_post_reviews = obj.review_set.all()
serializer = BlogPostReviewSerializer(blog_post_reviews, many=True)
return serializer.data
This error comes
in get_blog_post_reviews
blog_post_reviews = obj.review_set.all()
AttributeError: 'BlogPost' object has no attribute 'review_set'
How to solve this problem or what I'm doing wrong and what need to be fixed. What would be another apporach obv there would be.... And I don't know why Dennis Ivy used review_set in his code. If someone know why we use _set and what are the circumstances please let me know.
The simplest solution is to update your get_blog_post_reviews method:
def get_blog_post_reviews(self, obj):
blog_post_reviews = obj.blogpostreview_set.all() # <- this line has changed
serializer = BlogPostReviewSerializer(blog_post_reviews, many=True)
return serializer.data
The original worked because there was a model named Review, so the automatically created reverse name was review_set. Your model is named BlogPostReview, so the reverse is blogpostreview_set.
More information about reverse relationships in the docs.

how to fix django instance of a model

Error message AttributeError: 'NoneType' object has no attribute 'add'
am trying to create an instance of a model.
the card model is suppose to be an instance of the patient model and the patient model has a foreign key relations with the user model.
Its like a patient who has a card in the hospital.
My error is coming from the perform_create method
views.py
class PatientCardListAPIView(ListCreateAPIView):
serializer_class = PatientsCardSerializer
queryset = Card.objects.all()
permission_classes = (permissions.IsAuthenticated, IsOwner, )
def perform_create(self, serializer):
serializer.save()
serializer.instance.owner.add(self.request.user)
models.py
from django.db import models
from authentication.models import User
# Create your models here.
class Patient(models.Model):
name = models.CharField(max_length=255, null=True)
country = models.CharField(max_length=255, null=True)
state = models.CharField(max_length=255, null=True)
phone = models.CharField(max_length=255, null=True)
email = models.CharField(max_length=255, null=True)
owner = models.ForeignKey(to=User, null=True, on_delete=models.CASCADE)
def __str__(self):
return self.name
class Card(models.Model):
name = models.CharField(max_length=255, null=True)
card_number = models.CharField(max_length=255, null=True)
owner = models.OneToOneField(Patient, null=True, blank=True, on_delete=models.CASCADE)
def __str__(self):
return self.name
It should be as
class PatientCardListAPIView(ListCreateAPIView):
serializer_class = PatientsCardSerializer
queryset = Card.objects.all()
permission_classes = (permissions.IsAuthenticated, IsOwner,)
def perform_create(self, serializer):
serializer.save(owner = self.request.user)
This should be:
def perform_create(self, serializer):
model_instance = serializer.save(owner=self.request.user)
OR
def perform_create(self, serializer):
model_instance = serializer.save()
model_instance.owner = self.request.user
Edited
..............
A closer look at your model shows that you need to redefine the model. As you've defined, the owner field under Patient class indicated that you can have a User who can be many Patient...(a query of: patient=Patient.objects.get(owner=self.request.user) may return more than one instance. ). Suggested model should be:
class Patient(models.Model):
name = models.CharField(max_length=255, null=True)
country = models.CharField(max_length=255, null=True)
state = models.CharField(max_length=255, null=True)
phone = models.CharField(max_length=255, null=True)
email = models.CharField(max_length=255, null=True)
**owner = models.OneToOneField(User, null=True)**
def __str__(self):
return self.name
class Card(models.Model):
name = models.CharField(max_length=255, null=True)
card_number = models.CharField(max_length=255, null=True)
owner = models.OneToOneField(Patient, null=True, blank=True, on_delete=models.CASCADE)
def __str__(self):
return self.name
Then you can have your view:
class PatientCardListAPIView(ListCreateAPIView):
serializer_class = PatientsCardSerializer
queryset = Card.objects.all()
permission_classes = (permissions.IsAuthenticated, IsOwner, )
def perform_create(self, serializer):
card_instance = serializer.save()
patient = Patient.objects.get(owner=self.request.user)
card_instance.owner = patient
Appreciation
I was almost discouraged with programming for the past 24hrs because of this problem, but this community helped me, even though they did not give the answer i needed, they led me into discovering the answer myself, and i say Thank you to stackoverflow community...
Solved
I finally discovered what i was doing wrong.
a card does not just belong to a patient, it also belongs to a hospital, so the card needs a relation to both the hospital and the patient.
models.py
class Patient(models.Model):
name = models.CharField(max_length=255, null=True)
country = models.CharField(max_length=255, null=True)
state = models.CharField(max_length=255, null=True)
phone = models.CharField(max_length=255, null=True)
email = models.CharField(max_length=255, null=True)
owner = models.ForeignKey(to=User, null=True, on_delete=models.CASCADE)
def __str__(self):
return self.name
class Card(models.Model):
name = models.CharField(max_length=255, null=True)
card_number = models.CharField(max_length=255, null=True)
owner = models.OneToOneField(Patient, null=True, blank=True, on_delete=models.CASCADE)
hospital = models.ForeignKey(User, null=True, blank=True, on_delete=models.CASCADE)
def __str__(self):
return self.name
views.py
class PatientCardListAPIView(ListCreateAPIView):
serializer_class = PatientsCardSerializer
queryset = Card.objects.all()
permission_classes = (permissions.IsAuthenticated, IsOwner, )
def perform_create(self, serializer):
return serializer.save(hospital=self.request.user)
def get_queryset(self):
return self.queryset.filter(hospital=self.request.user)
class PatientCardDetailAPIView(RetrieveUpdateDestroyAPIView):
serializer_class = PatientsCardSerializer
permission_classes = (permissions.IsAuthenticated, IsOwner,)
queryset = Card.objects.all()
lookup_field = "id"
def get_queryset(self):
return self.queryset.filter(hospital=self.request.user)

model linked with model through M2O relation which is further linked with model through M2O relation raises error

I am working on an online-shop in django.
I have linked the order model with the cart model through ForeignKey which is further linked with products model through ForeignKey.
models.py:
class products(models.Model):
image = models.ImageField(upload_to='products/')
name = models.CharField(max_length=50)
slug = models.SlugField(blank=True, unique=True)
title = models.CharField(max_length=50)
price = models.FloatField()
def __str__(self):
return self.name
class cart(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
item = models.ForeignKey(products, on_delete=models.CASCADE) ###
slug = models.CharField(max_length=50, default='#')
quantity = models.IntegerField(default=1)
created_on = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f'{self.quantity} of {self.item.name}'
def get_total(self):
total = self.item.price * self.quantity
floattotal = float("{0:.2f}".format(total))
return floattotal
class order(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
item = models.ForeignKey(cart, on_delete=models.CASCADE) ###
slug = models.SlugField()
quantity = models.IntegerField()
created_on = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f'{self.quantity} of {self.item.item__name}'
I wanted to create object of order as:
def order_view(request, slug):
cart_qs = cart.objects.filter(user=request.user, slug=slug)
cart_item = cart_qs[0]
order.objects.create(user=request.user, item=cart_item.item.name, slug=slug, quantity=cart_item.quantity) ####
It raises error as:
Cannot assign "'The latest one'": "order.item" must be a "cart" instance.
Why this error arises and how can I resolve this?
You are trying to assign string on Order instance creation where it's expecting a Cart instance. The error raised here:
item=cart_item.item.name
It should be simply:
item=cart_item
, because your model schema expect this:
item = models.ForeignKey(cart, on_delete=models.CASCADE) ###

Categories

Resources