creating post on a review model - python

I need help leaving a review to a another model as a user, ive been having errrors and tried a lot of solutions.
error
FieldError at /reviews/1
Cannot resolve keyword 'user' into field. Choices are: id, pet_owner, pet_owner_id, rating, review, sitter, sitter_id
This is my view function:
def post(self, request, pk):
user = Review(pet_owner = request.user)
sitter = get_object_or_404(Sitter, pk=pk)
data = request.data
review = Review.objects.create(
pet_owner_id = user,
sitter= sitter,
rating=data['rating'],
review=data['review']
)
return Response('Review Added', status=status.HTTP_400_BAD_REQUEST)
and these are the models:
class Review(models.Model):
review = models.CharField(max_length=500)
rating = models.DecimalField(max_digits=7, decimal_places=2)
sitter = models.ForeignKey(Sitter,on_delete=models.CASCADE,null=True)
pet_owner = models.ForeignKey(get_user_model(),on_delete=models.CASCADE,null=True)
class Sitter(models.Model):
first_name = models.CharField(max_length=255, default='Jane')
last_name = models.CharField(max_length=255, default='Doe')
zipcode = models.CharField(max_length = 5, default='12345')
supersitter = models.BooleanField(null=True, blank=True)
price = models.DecimalField(max_digits=7, decimal_places=2)
numReviews = models.IntegerField(null=True, blank=True, default=0)
rating = models.DecimalField(max_digits=7, decimal_places=2, default=0)
this is my review model and sitter model
def post(self, request):
"""Create request"""
print(request.data)
# Add user to request data object
# Serialize/create review
review_user = request.user
review_data = Review(pet_owner = review_user)
review = ReviewSerializer(review_data, data=request.data)
# If the review data is valid according to our serializer...
if review.is_valid():
# Save the created review & send a response
r = review.save()
return Response({ 'review': review.data }, status=status.HTTP_201_CREATED)
# # If the data is not valid, return a response with the errors
return Response(review.data, status=status.HTTP_400_BAD_REQUEST)
we also tried this

user should be a simple User object, so:
def post(self, request, pk):
sitter = get_object_or_404(Sitter, pk=pk)
data = request.data
review = Review.objects.create(
pet_owner=request.user,
sitter=sitter,
rating=data['rating'],
review=data['review']
)
return Response('Review Added', status=status.HTTP_400_BAD_REQUEST)
You can slightly optimize the method by using the pk immediately:
def post(self, request, pk):
review = Review.objects.create(
pet_owner=request.user,
sitter_id=pk,
rating=data['rating'],
review=data['review']
)
return Response('Review Added', status=status.HTTP_400_BAD_REQUEST)
but it might be better to work with a serializer to properly validate and clean the request data.

Related

drf_yasg #swagger_auto_schema not showing the required parameters for POST Request

I am using django-yasg to create an api documentation. But no parameters are showing in the documentation to create post request. Following are my codes:
After that in swagger api, no parameters are showing for post request to create the event
model.py
class Events(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
category = models.ForeignKey(EventCategory, on_delete=models.CASCADE)
name = models.CharField(max_length=250, null=True)
posted_at = models.DateTimeField(null=True, auto_now=True)
location = models.CharField(max_length=100, null=True)
banner = models.ImageField(default='avatar.jpg', upload_to='Banner_Images')
start_date = models.DateField(
auto_now=False, auto_now_add=False, blank=True, null=True)
end_date = models.DateField(
auto_now=False, auto_now_add=False, blank=True, null=True)
description = models.TextField(max_length=2000, null=True)
completed = models.BooleanField(default=False)
def __str__(self):
return f'{self.name}'
class Meta:
verbose_name_plural = "Events"
serializers.py
class EventSerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=True, many=False)
category = EventCategorySerializer(read_only=True, many=False)
class Meta:
model = Events
fields = '__all__'
views.py
#api_view(['POST'])
#permission_classes([IsAuthenticated])
#user_is_organization
#swagger_auto_schema(
request_body=EventSerializer
)
def registerEvent(request):
"""
To Register an events, you must be an organization
"""
data = request.data
print("==================")
print(data)
print("==================")
try:
Event = Events.objects.create(
user = request.user,
category=EventCategory.objects.get(category=data['category']),
name=data['name'],
location=data['location'],
start_date=data['start_date'],
end_date=data['end_date'],
description=data['description'],
completed=data['completed'],
)
serializer = EventSerializer(Event, many=False)
Event = Events.objects.get(id=serializer.data['id'])
Event.banner = request.FILES.get('banner')
Event.save()
serializer = EventSerializer(Event, many=False)
return Response(serializer.data)
except:
message = {'detail': 'Event with this content already exists'}
return Response(message, status=status.HTTP_400_BAD_REQUEST)
I got it running with following changes in views.py
#swagger_auto_schema(
methods=['post'],
request_body=openapi.Schema(
type=openapi.TYPE_OBJECT,
required=['category','name', 'location', 'start_date', 'end_date', 'description', 'completed', 'banner'],
properties={
'category':openapi.Schema(type=openapi.TYPE_STRING),
'name':openapi.Schema(type=openapi.TYPE_STRING),
'location':openapi.Schema(type=openapi.TYPE_STRING),
'start_date':openapi.Schema(type=openapi.TYPE_STRING, default="yyyy-mm-dd"),
'end_date':openapi.Schema(type=openapi.TYPE_STRING, default='yyyy-mm-dd'),
'description':openapi.Schema(type=openapi.TYPE_STRING),
'completed':openapi.Schema(type=openapi.TYPE_BOOLEAN, default=False),
'banner': openapi.Schema(type=openapi.TYPE_FILE),
},
),
operation_description='Create an events'
)
#api_view(['POST'])
#permission_classes([IsAuthenticated])
#user_is_organization
def registerEvent(request):
"""
To Register an events, you must be an organization
"""
data = request.data
print("==================")
print(data)
print(type(data))
category=EventCategory.objects.get(category=data['category']),
print(category)
print(type(data["start_date"]))
print("==================")
try:
Event = Events.objects.create(
user = request.user,
category=EventCategory.objects.get(category=data['category']),
name=data['name'],
location=data['location'],
start_date=data['start_date'],
end_date=data['end_date'],
description=data['description'],
completed=data['completed'],
)
print("****************************")
serializer = EventSerializer(Event, many=False)
Event = Events.objects.get(id=serializer.data['id'])
Event.banner = request.FILES.get('banner')
Event.save()
serializer = EventSerializer(Event, many=False)
return Response(serializer.data)
except ValidationError as e:
return Response({"ValidationError" : e}, status = status.HTTP_400_BAD_REQUEST)
except Exception as e:
message = {'error': e}
return Response(message, status=status.HTTP_400_BAD_REQUEST)
can you modify your swagger_auto_schema decorator like this?
#swagger_auto_schema(
methods=['post',],
request_body=EventSerializer )
All you need to do is place the #swagger_auto_schema on the top. To help it display the parameters create a serializer for it. This way, you only have to change the serializer in the future keeping everything in one place.
API view
#swagger_auto_schema(request_body=serializers.RequestSerializer, method='post')
#api_view(http_method_names=['POST'])
def special_get(request):
data = JSONParser().parse(request)
unique_id = data.get("unique_id", "")
...
Serializer.py
class RequestSerializer(serializers.Serializer):
unique_id = serializers.CharField(max_length=50, allow_null=False, allow_blank=True)
sentence_list = serializers.ListField(
child=serializers.CharField(allow_blank=False, trim_whitespace=True)
)

Better way to save data in database using django serializer

Hello I am working on a project in which I'm making serializers
and I send post requests using postman. This is working fine but this is a very small part of the application and the code will grow to be very large. If there is a better way of doing this in which I write smaller code then I would like to employ that.
Right now the way I'm saving the information is as follows
models.py
class Services(models.Model):
business = models.ForeignKey(Business, on_delete=models.CASCADE)
service = models.CharField(max_length=30, blank=True)
serializers.py
class ServiceSerializer(serializers.ModelSerializer):
class Meta:
model = Services
fields = ('id', 'business', 'service')
views.py
class ServiceAPIView(APIView):
permission_classes = [IsAuthenticated]
def post(self, request):
data = request.data
business = Business.objects.get(user=request.user)
data['business'] = business.pk
serializer = BusinessSerializer(data=data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
urls.py
urlpatterns = [
path('service/', ServiceAPIView.as_view()),
]
Edited*
Here is the business model
class Business(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
business_name = models.CharField(max_length=50, blank=True)
invoices_terms = models.TextField(max_length=100, blank=True)
desc = models.TextField(max_length=1000, blank=True)
website = models.URLField(max_length=200, blank=True)
tax_percentage = models.DecimalField(blank=True, max_digits=5, decimal_places=3)
labour_rate_per_hour = models.DecimalField(blank=True, max_digits=5, decimal_places=2)
tax_hst_number = models.IntegerField(blank=True)
Well the code looks ok and it's also ok to have deep modules.
A little update could be the following:
class ServiceAPIView(APIView):
permission_classes = [IsAuthenticated,]
serializer_class = ServiceSerializer
def post(self, request):
data = request.data
business = Business.objects.get(user=request.user)
data['business'] = business.pk
serializer = self.get_serializer(data=data)
serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
In this way you can write only one Response line for each view request method and avoid to use if/else statements

Add count whenever any entity is fetched Django REST Framework

My model looks like
class Article(models.Model):
article_type = models.ForeignKey(
ArticleType,
on_delete=models.CASCADE,
related_name='articles'
)
title = models.CharField(
max_length=100,
verbose_name='Article Title'
)
count = models.IntegerField(
verbose_name='Frequency Count'
)
def __str__(self):
return self.title
and my urls.py
router = DefaultRouter()
router.register('article', ArticleViewSet, basename='article')
urlpatterns = [
path('viewset/', include(router.urls)),
]
Now I wan't to add functionality such that whenever any article is fetched i.e
http://127.0.0.1:8000/viewset/article/{pk}
than 'count' of article of id=pk becomes count = count+1
so that I can sort them according to this count.
This can be achieved by sending a request like fetch=true or seen=true from client side whenever the api been fetched by the client i.e the client will sent you
fetch=true whenever fetch the api and from backend you have to catch that flag and have to check if fetch=true and increase instance.count += 1 and save the change in your model.
CODE: First change your model by providing count field a default value, this time its 0.
class Article(models.Model):
article_type = models.ForeignKey(
ArticleType,
on_delete=models.CASCADE,
related_name='articles'
)
title = models.CharField(
max_length=100,
verbose_name='Article Title'
)
count = models.IntegerField(
verbose_name='Frequency Count',
default=0
)
def __str__(self):
return self.title
and then do migration.
and then VIEW
class ArticleViewSet(viewsets.ViewSet):
def retrieve(self, request, pk=None):
queryset = Article.objects.all()
fetch = request.GET.get('fetch', False)
article = get_object_or_404(queryset, pk=pk)
if fetch:
article.count += 1
article.save()
serializer = ArticleSerializer(article)
return Response(serializer.data)
and then request with article/1/?fetch=true
you can also do it without fetch flag too,
class ArticleViewSet(viewsets.ViewSet):
def retrieve(self, request, pk=None):
queryset = Article.objects.all()
article = get_object_or_404(queryset, pk=pk)
article.count += 1
article.save()
serializer = ArticleSerializer(article)
return Response(serializer.data)
now reqeust with article/1/
I figured out from #Tanvir's answer
When we retrieve object we can update the count there itself i.e
def retrieve(self, request):
obj = self.get_object()
obj.count = obj.count+1
obj.save()
serializer = self.get_serializer(obj)
return Response(serializer.data)

MultipleObjectsReturned at /checkout/ get() returned more than one Order -- it returned 2

Hi I'm trying to develop an e-commerce website using Django. I have two models and views on separate folders, one is an order model and view, another is the checkout model and view. The order view creates a new order everytime an item is added to the cart. And the checkout view creates a new billing address. I want to associate the billing address that is created to the order when the checkout form is submitted. But for some reason, it's not happening. It throws an error:
MultipleObjectsReturned at /checkout/
get() returned more than one Order -- it returned 2!
What is the problem?
My orders.models.py:
from django.db import models
from shopping_cart.models import Cart
from django.contrib.auth import get_user_model
from accounts2.models import BillingAddress
STATUS_CHOICES = (
("Started", "Started"),
("Abandoned", "Abandoned"),
("Finished", "Finished"),
)
User = get_user_model()
class Order(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True)
order_id = models.CharField(max_length=120, default='ABC', unique=True)
cart = models.ForeignKey(Cart, on_delete=models.CASCADE)
status = models.CharField(max_length=120, choices=STATUS_CHOICES, default="Started")
sub_total = models.DecimalField(default=10.99, max_digits=1000, decimal_places=2)
tax_total = models.DecimalField(default=0.00, max_digits=1000, decimal_places=2)
final_total = models.DecimalField(default=10.99, max_digits=1000, decimal_places=2)
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
billing_address = models.ForeignKey(BillingAddress, on_delete=models.SET_NULL, blank= True, null=True)
My orders.views.py:
#login_required
def order(request):
try:
the_id = request.session['cart_id']
cart = Cart.objects.get(id=the_id)
except:
the_id = None
return redirect(reverse("myshop-home"))
try:
new_order = Order.objects.get(cart=cart)
except Order.DoesNotExist:
new_order = Order(cart=cart)
new_order.cart = cart
new_order.user = request.user
new_order.order_id = id_generator()
new_order.save()
except:
return redirect(reverse("cart"))
if new_order.status == "Finished":
#cart.delete()
del request.session['cart_id']
del request.session['items_total']
return redirect(reverse("cart"))
context = {"address_form": address_form, "cart": cart}
template = "orders/checkout.html"
return render(request, template, context)
My accounts.models.py:
class BillingAddress(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete= models.CASCADE)
street_address = models.CharField(max_length=100)
apartment_address = models.CharField(max_length=100)
country = CountryField(multiple=False)
zip = models.CharField(max_length=100)
class Meta:
verbose_name_plural = "Billing Addresses"
My accounts.views.py:
class CheckoutView(LoginRequiredMixin, View):
def get(self, *args, **kwargs):
the_id = self.request.session['cart_id']
cart = Cart.objects.get(id=the_id)
form = CheckoutForm()
context = {"form": form, "cart": cart}
return render(self.request, "orders/checkout.html", context)
def post(self, *args, **kwargs):
form = CheckoutForm(self.request.POST or None)
try:
order = Order.objects.get(user = self.request.user)
if form.is_valid():
street_address = form.cleaned_data.get('street_address')
apartment_address = form.cleaned_data.get('apartment_address')
country = form.cleaned_data.get('country')
zip = form.cleaned_data.get('zip')
# same_shipping_address = form.cleaned_data.get('same_billing_address')
# save_info = form.cleaned_data.get('save_info')
payment_option = form.cleaned_data.get('payment_option')
billing_address = BillingAddress(
user = self.request.user,
street_address = street_address,
apartment_address = apartment_address,
country = country,
zip = zip
)
billing_address.save()
order.billing_address = billing_address
order.save()
return redirect('checkout')
messages.warning(self.request, "Failed checkout")
return redirect('checkout')
except ObjectDoesNotExist:
messages.warning(self.request, "You do not have an active order")
return redirect('/')
Use Order.objects.filter(user=user).last() to get the newest row or try to remember order's id when creating an order and filter with Order.objects.get(id=id)

How to create a like functionality in django for a blog

I have to build a blog for my chess club and I would like to make a like functionality for the posts. When the user likes the post the like count increases and the post its shown in the user profile.
Here is what I made so far:
This is my post model:
class Post(models.Model):
title = models.CharField(max_length=100)
overview = models.TextField()
timestamp = models.DateTimeField(auto_now_add=True)
content = HTMLField()
is_aproved = models.BooleanField(default=False)
# comment_count = models.IntegerField(default = 0)
# view_count = models.IntegerField(default = 0)
slug = models.SlugField(unique=True, blank=True)
likes = models.ManyToManyField(
settings.AUTH_USER_MODEL, blank=True, related_name='post_likes')
author = models.ForeignKey(Author, on_delete=models.CASCADE)
thumbnail = models.ImageField()
categories = models.ManyToManyField(Category)
featured = models.BooleanField()
previous_post = models.ForeignKey(
'self', related_name='previous', on_delete=models.SET_NULL, blank=True, null=True)
next_post = models.ForeignKey(
'self', related_name='next', on_delete=models.SET_NULL, blank=True, null=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={
'id': self.id
})
def get_update_url(self):
return reverse('post-update', kwargs={
'id': self.id
})
def get_delete_url(self):
return reverse('post-delete', kwargs={
'id': self.id
})
#property
def get_comments(self):
return self.comments.all().order_by('-timestamp')
#property
def comment_count(self):
return Comment.objects.filter(post=self).count()
#property
def view_count(self):
return PostView.objects.filter(post=self).count()
And this is my view:
def post(request, id):
category_count = get_category_count()
most_recent = Post.objects.order_by('-timestamp')[:3]
post = get_object_or_404(Post, id=id)
if request.user.is_authenticated:
PostView.objects.get_or_create(user=request.user, post=post)
form = CommentForm(request.POST or None)
if request.method == "POST":
if form.is_valid():
form.instance.user = request.user
form.instance.post = post
form.save()
return redirect(reverse("post-detail", kwargs={
'id': post.pk
}))
context = {
'form': form,
'post': post,
'most_recent': most_recent,
'category_count': category_count,
'form': form
}
return render(request, 'post.html', context)
could you help me to implement this like functionality
You need another model to get liking functionality in Django. Create a like model like this-
class PostLikes(models.Model):
user = models.ForeignKey(User)
post = models.ForeignKey(Post)
created = models.DateTimeField(auto_now_add=True)
then create a view to add the instance to the PostLikes model-
def like_others_post(request, post_id):
new_like, created = PostLikes.objects.get_or_create(user=request.user,
post_id=post_id)
if not created:
# you may get and delete the object as the user may already liked this
post before
We are using get or create to avoid duplication of likes. So a user may click multiple times but, a like instance is stored only once.
then add a method to Post model for getting the number of likes-
def Post(models.Model):
...
...
#property
def view_count(self):
return PostLikes.objects.filter(post=self).count()
To find out if the current user already likes the displayed post or not, in your post view-
def post(request, id):
....
....
already_liked = PostLikes.objects.filter(user=request.user, post=post).count()
if number_of_likes > 0:
already_liked = True # pass this variable to your context
else:
already_liked = False # you can make this anything other than boolean
Then by use of Javascript and {{already_liked}}, change your animation of like button in your template.

Categories

Resources