Screenshot of the Error is at Error
The Error I face is:
UnboundLocalError at /api/registration/
local variable 'data' referenced before assignment
Request Method: POST
Request URL: http://217.160.170.83:81/api/registration/
Django Version: 3.2.12
Exception Type: UnboundLocalError
Exception Value:
local variable 'data' referenced before assignment
Exception Location: /var/www/LWD/userAccount/views.py, line 128, in get_response_data
Python Executable: /usr/bin/python3
Python Version: 3.8.10
Python Path:
['/var/www/LWD',
'/usr/lib/python38.zip',
'/usr/lib/python3.8',
'/usr/lib/python3.8/lib-dynload',
'/usr/local/lib/python3.8/dist-packages',
'/usr/lib/python3/dist-packages']
Server time: Sat, 26 Mar 2022 19:05:05 +0000
My Project Repository Link: https://github.com/Bilal815/LWD
LWD/userAccount/views.py:
from django.conf import settings
from django.shortcuts import get_object_or_404
from rest_framework.response import Response
from rest_framework import permissions, status, viewsets
from rest_framework.views import APIView
from rest_framework.generics import (
ListAPIView,
RetrieveAPIView,
CreateAPIView,
GenericAPIView,
RetrieveUpdateAPIView,
UpdateAPIView,
)
from rest_framework.exceptions import PermissionDenied, NotAcceptable, ValidationError
from allauth.account.views import ConfirmEmailView
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
from allauth.socialaccount.providers.twitter.views import TwitterOAuthAdapter
from allauth.socialaccount.providers.google.views import GoogleOAuth2Adapter
from allauth.socialaccount.providers.oauth2.client import OAuth2Client
from rest_auth.registration.views import SocialConnectView, SocialLoginView
from rest_auth.social_serializers import TwitterConnectSerializer
from allauth.account.models import EmailAddress, EmailConfirmationHMAC
from rest_auth.views import (
LoginView,
PasswordResetView,
PasswordResetConfirmView,
PasswordChangeView,
LogoutView,
)
from rest_auth.serializers import PasswordResetConfirmSerializer
from rest_auth.registration.views import RegisterView, VerifyEmailView
from rest_auth.registration.serializers import VerifyEmailSerializer
from rest_auth.app_settings import JWTSerializer
from rest_auth.utils import jwt_encode
from django.views.decorators.debug import sensitive_post_parameters
from django.utils.decorators import method_decorator
from django.contrib.auth.models import User, Permission
from django.utils.translation import ugettext_lazy as _
from .models import Profile, Address, SMSVerification, DeactivateUser, NationalIDImage
from .serializers import (
ProfileSerializer,
UserSerializer,
AddressSerializer,
CreateAddressSerializer,
SMSVerificationSerializer,
SMSPinSerializer,
DeactivateUserSerializer,
PermissionSerializer,
PasswordChangeSerializer,
UserPermissionSerializer,
NationalIDImageSerializer,
)
from .send_mail import send_register_mail, send_reset_password_email
sensitive_post_parameters_m = method_decorator(
sensitive_post_parameters("password1", "password2")
)
class DeactivateUserView(CreateAPIView):
permission_classes = [permissions.IsAuthenticated]
serializer_class = DeactivateUserSerializer
def create(self, request, *args, **kwargs):
user = request.user
# TODO validation and try exception
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save(user=user)
return Response("your account will deactivate after 30 days.")
class CanselDeactivateUserView(APIView):
permission_classes = [permissions.IsAuthenticated]
def post(self, request, *args, **kwargs):
user = request.user
# TODO validation and try exception
deactivate = DeactivateUser.objects.get(user=user)
deactivate.deactive = False
deactivate.save()
user.is_active = True
user.save()
return Response("your account will activated.")
class LoginAPIView(LoginView):
queryset = ""
def get_response(self):
serializer_class = self.get_response_serializer()
if getattr(settings, "REST_USE_JWT", False):
data = {"user": self.user, "token": self.token}
serializer = serializer_class(
instance=data, context={"request": self.request}
)
else:
serializer = serializer_class(
instance=self.token, context={"request": self.request}
)
response = Response(serializer.data, status=status.HTTP_200_OK)
deactivate = DeactivateUser.objects.filter(user=self.user, deactive=True)
if deactivate:
deactivate.update(deactive=False)
return response
def post(self, request, *args, **kwargs):
self.request = request
self.serializer = self.get_serializer(
data=self.request.data, context={"request": request}
)
self.serializer.is_valid(raise_exception=True)
self.login()
return self.get_response()
class RegisterAPIView(RegisterView):
#sensitive_post_parameters_m
def dispatch(self, *args, **kwargs):
return super(RegisterAPIView, self).dispatch(*args, **kwargs)
def get_response_data(self, user):
if getattr(settings, "REST_USE_JWT", False):
data = {"user": user, "token": self.token}
return JWTSerializer(data).data
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(
self.get_response_data(user),
status=status.HTTP_201_CREATED,
headers=headers,
)
def perform_create(self, serializer):
user = serializer.save(self.request)
if getattr(settings, "REST_USE_JWT", False):
self.token = jwt_encode(user)
email = EmailAddress.objects.get(email=user.email, user=user)
confirmation = EmailConfirmationHMAC(email)
key = confirmation.key
# TODO Send mail confirmation here .
# send_register_mail.delay(user, key)
print("account-confirm-email/" + key)
return user
class ResendSMSAPIView(GenericAPIView):
permission_classes = (permissions.AllowAny,)
serializer_class = SMSVerificationSerializer
allowed_methods = ("POST",)
def resend_or_create(self):
phone = self.request.data.get("phone")
send_new = self.request.data.get("new")
sms_verification = None
user = User.objects.filter(profile__phone_number=phone).first()
if not send_new:
sms_verification = (
SMSVerification.objects.filter(user=user, verified=False)
.order_by("-created")
.first()
)
if sms_verification is None:
sms_verification = SMSVerification.objects.create(user=user, phone=phone)
return sms_verification.send_confirmation()
def post(self, request, *args, **kwargs):
success = self.resend_or_create()
return Response(dict(success=success), status=status.HTTP_200_OK)
class VerifySMSView(APIView):
permission_classes = (permissions.AllowAny,)
allowed_methods = ("POST", "OPTIONS", "HEAD")
def get_serializer(self, *args, **kwargs):
return SMSPinSerializer(*args, **kwargs)
def post(self, request, pk):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
pin = int(request.data.get("pin"))
# TODO get user SMSVerification instead of below confirmation variable
confirmation = get_object_or_404(SMSVerification, pk=pk)
confirmation.confirm(pin=pin)
return Response("Your Phone Number Is Verfied.", status=status.HTTP_200_OK)
class ProfileAPIView(APIView):
permission_classes = [permissions.IsAuthenticated]
def get(self, request, pk):
profile = Profile.objects.get(pk=pk)
serializer = ProfileSerializer(profile, context={"request": request})
return Response(serializer.data, status=status.HTTP_200_OK)
class UserDetailView(RetrieveAPIView):
permission_classes = [permissions.IsAuthenticated]
serializer_class = UserSerializer
queryset = User.objects.all()
lookup_field = "username"
class ListAddressAPIView(ListAPIView):
permission_classes = [permissions.IsAuthenticated]
serializer_class = AddressSerializer
def get_queryset(self):
user = self.request.user
queryset = Address.objects.filter(user=user)
return queryset
class AddressDetailView(RetrieveAPIView):
permission_classes = [permissions.IsAuthenticated]
serializer_class = AddressSerializer
queryset = Address.objects.all()
def retrieve(self, request, *args, **kwargs):
user = request.user
address = self.get_object()
if address.user != user:
raise NotAcceptable("this addrss don't belong to you")
serializer = self.get_serializer(address)
return Response(serializer.data, status=status.HTTP_200_OK)
class createAddressAPIView(CreateAPIView):
permission_classes = [permissions.IsAuthenticated]
serializer_class = CreateAddressSerializer
queryset = ""
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save(user=request.user, primary=True)
return Response(serializer.data, status=status.HTTP_201_CREATED)
class FacebookConnectView(SocialLoginView):
adapter_class = FacebookOAuth2Adapter
class TwitterConnectView(SocialLoginView):
serializer_class = TwitterConnectSerializer
adapter_class = TwitterOAuthAdapter
class GoogleLogin(SocialLoginView):
adapter_class = GoogleOAuth2Adapter
client_class = OAuth2Client
callback_url = "https://www.google.com"
class PasswordResetView(APIView):
def post(self, request, *args, **kwargs):
email = request.data.get("email", None)
try:
user = User.objects.get(email=email)
except User.DoesNotExist:
raise NotAcceptable(_("Please enter a valid email."))
send_reset_password_email.delay(user)
return Response(
{"detail": _("Password reset e-mail has been sent.")},
status=status.HTTP_200_OK,
)
class PasswordResetConfirmView(GenericAPIView):
permission_classes = (permissions.AllowAny,)
serializer_class = PasswordResetConfirmSerializer
#sensitive_post_parameters_m
def dispatch(self, *args, **kwargs):
return super(PasswordResetConfirmView, self).dispatch(*args, **kwargs)
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response({"detail": _("Password has been reset with the new password.")})
class PasswordChangeView(GenericAPIView):
permission_classes = (permissions.IsAuthenticated,)
serializer_class = PasswordChangeSerializer
#sensitive_post_parameters_m
def dispatch(self, *args, **kwargs):
return super(PasswordChangeView, self).dispatch(*args, **kwargs)
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response({"detail": _("Congratulations, password has been Changed.")})
class VerifyEmailView(APIView, ConfirmEmailView):
permission_classes = (permissions.AllowAny,)
allowed_methods = ("POST", "OPTIONS", "HEAD")
def get_serializer(self, *args, **kwargs):
return VerifyEmailSerializer(*args, **kwargs)
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.kwargs["key"] = serializer.validated_data["key"]
confirmation = self.get_object()
confirmation.confirm(self.request)
return Response({"detail": _("ok")}, status=status.HTTP_200_OK)
class RetrievePermissionView(RetrieveAPIView):
serializer_class = UserPermissionSerializer
queryset = User.objects.all()
lookup_field = "username"
class UpdatePermissionView(UpdateAPIView):
serializer_class = UserPermissionSerializer
queryset = User.objects.all()
lookup_field = "username"
def partial_update(self, request, *args, **kwargs):
kwargs["partial"] = True
return self.update(request, *args, **kwargs)
# def update(self, request, *args, **kwargs):
# partial = True
# return super(UpdatePermissionView, self).update(request, *args, **kwargs)
class NationalIDImageViewSet(viewsets.ModelViewSet):
serializer_class = NationalIDImageSerializer
queryset = NationalIDImage.objects.all().select_related("user")
LWD/userAccounts/models.py:
import logging
from datetime import datetime, timezone, timedelta
from django.db import models
from django.contrib.auth import get_user_model
from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver
from django.conf import settings
from django.core.cache import cache
from django.core.validators import MaxValueValidator, MinValueValidator, RegexValidator
from rest_framework.authtoken.models import Token
from rest_framework.exceptions import NotAcceptable
from allauth.account.signals import user_signed_up
from phonenumber_field.modelfields import PhoneNumberField
from django_countries.fields import CountryField
from randompinfield import RandomPinField
import phonenumbers
from twilio.rest import Client
from twilio.base.exceptions import TwilioRestException
from .signals import register_signal
from .managers import NationalIDImageManager
from core.models import TimeStampedModel
from core.handle_images import compress_image
User = get_user_model()
def user_directory_path(instance, filename):
# file will be uploaded to MEDIA_ROOT/users/<username>/<filename>
return "users/{0}/{1}".format(instance.user.username, filename)
def national_image_path(instance, filename):
return f"national/{instance.user.username}/images/{filename}"
class Profile(TimeStampedModel):
GENDER_MALE = "m"
GENDER_FEMALE = "f"
OTHER = "o"
GENDER_CHOICES = (
(GENDER_MALE, "Male"),
(GENDER_FEMALE, "Female"),
(OTHER, "Other"),
)
user = models.OneToOneField(User, related_name="profile", on_delete=models.CASCADE)
profile_picture = models.ImageField(upload_to=user_directory_path, blank=True)
phone_number = PhoneNumberField(blank=True)
gender = models.CharField(max_length=1, choices=GENDER_CHOICES, blank=True)
about = models.TextField(blank=True, null=True)
birth_date = models.DateField(blank=True, null=True)
def __str__(self):
return "%s" % self.user.username
#property
def last_seen(self):
return cache.get(f"seen_{self.user.username}")
#property
def online(self):
if self.last_seen:
now = datetime.now(timezone.utc)
if now > self.last_seen + timedelta(minutes=settings.USER_ONLINE_TIMEOUT):
return False
else:
return True
else:
return False
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, *args, **kwargs):
if created:
Profile.objects.create(user=instance)
class Address(TimeStampedModel):
user = models.ForeignKey(User, related_name="address", on_delete=models.CASCADE)
country = CountryField(blank=False, null=False)
city = models.CharField(max_length=100, blank=False, null=False)
district = models.CharField(max_length=100, blank=False, null=False)
street_address = models.CharField(max_length=250, blank=False, null=False)
postal_code = models.CharField(max_length=20, blank=True, null=True)
primary = models.BooleanField(default=False)
phone_number = PhoneNumberField(null=True, blank=True)
building_number = models.IntegerField(
blank=True, null=True, validators=[MinValueValidator(1)]
)
apartment_number = models.IntegerField(
blank=True, null=True, validators=[MinValueValidator(1)]
)
class SMSVerification(TimeStampedModel):
user = models.OneToOneField(User, related_name="sms", on_delete=models.CASCADE)
verified = models.BooleanField(default=False)
pin = RandomPinField(length=6)
sent = models.BooleanField(default=False)
phone = PhoneNumberField(null=False, blank=False)
def send_confirmation(self):
logging.debug("Sending PIN %s to phone %s" % (self.pin, self.phone))
if all(
[
settings.TWILIO_ACCOUNT_SID,
settings.TWILIO_AUTH_TOKEN,
settings.TWILIO_FROM_NUMBER,
]
):
try:
twilio_client = Client(
settings.TWILIO_ACCOUNT_SID, settings.TWILIO_AUTH_TOKEN
)
twilio_client.messages.create(
body="Your forgeter activation code is %s" % self.pin,
to=str(self.user.profile.phone_number),
from_=settings.TWILIO_FROM_NUMBER,
)
self.sent = True
self.save()
return True
except TwilioRestException as e:
logging.error(e)
else:
logging.warning("Twilio credentials are not set")
def confirm(self, pin):
if pin == self.pin and self.verified == False:
self.verified = True
self.save()
else:
raise NotAcceptable("your Pin is wrong, or this phone is verified before.")
return self.verified
#receiver(post_save, sender=Profile)
def send_sms_verification(sender, instance, *args, **kwargs):
try:
sms = instance.user.sms
if sms:
pin = sms.pin
sms.delete()
verification = SMSVerification.objects.create(
user=instance.user,
phone=instance.user.profile.phone_number,
sent=True,
verified=True,
pin=pin,
)
except:
if instance.user.profile.phone_number:
verification = SMSVerification.objects.create(
user=instance.user, phone=instance.user.profile.phone_number
)
# TODO Remove send confirm from here and make view for it.
verification.send_confirmation()
# if instance.user.profile.phone_number:
# verification = SMSVerification.objects.create(user=instance.user, phone=instance.user.profile.phone_number)
# # TODO Remove send confirm from here and make view for it.
# verification.send_confirmation()
class DeactivateUser(TimeStampedModel):
user = models.OneToOneField(
User, related_name="deactivate", on_delete=models.CASCADE
)
deactive = models.BooleanField(default=True)
class NationalIDImage(models.Model):
user = models.ForeignKey(
User, related_name="national_ids", on_delete=models.CASCADE
)
image = models.ImageField(upload_to=national_image_path, blank=True)
is_deleted = models.BooleanField(default=False)
objects = NationalIDImageManager()
def __str__(self):
return self.user.username
def save(
self,
force_insert=False,
force_update=False,
using=None,
update_fields=None,
*args,
**kwargs,
):
# if size greater than 300kb then it will send to compress image function
image = self.image
if image and image.size > (0.3 * 1024 * 1024):
self.image = compress_image(image)
super(NationalIDImage, self).save(*args, **kwargs)
P.S. I tried fixing it by commenting on the data clean-up for phone number and birthdate but it did not work. Things work well on local but I have no idea of what happened here on the server. This is my first time on the server and I need it done as I want to add this to my portfolio.
Please help!
Error is in this part:
def get_response_data(self, user):
if getattr(settings, "REST_USE_JWT", False):
data = {"user": user, "token": self.token}
return JWTSerializer(data).data
You only create data when if getattr(settings, "REST_USE_JWT", False) is true.
So what happens if it's false? data is not created, but you reference it in JWTSerializer(data).
You need to provide something in else like:
def get_response_data(self, user):
if getattr(settings, "REST_USE_JWT", False):
data = {"user": user, "token": self.token}
else:
data = {# something goes here}
return JWTSerializer(data).data
Related
I am building a web app using REST API and I have a 4 users that I want them to access only the content they have added to the backend. I have created 3 users say farmers(3 farmers) and added content using each one of them. However, I have been unable to implement permissions such that a farmer can view and delete only what they added.
Here's is my code in models.py
User = get_user_model()
# Create your models here.
class Tender(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
author = models.ForeignKey(User, on_delete=models.CASCADE)
description = models.TextField()
date_due = models.DateField(default=datetime.date.today)
location = models.CharField(max_length=255, null=False)
contact = models.PositiveIntegerField(null=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
class Input(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
author = models.ForeignKey(User, on_delete=models.CASCADE)
description = models.TextField()
price = models.DecimalField(max_digits=10, decimal_places=2)
quantity = models.DecimalField(max_digits=10, decimal_places=2)
contact = models.PositiveIntegerField(null=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
class Investor(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255, unique=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
description = models.TextField()
location = models.CharField(max_length=255, null=False)
contact = models.PositiveIntegerField(null=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
Here's what I implemented in permissions.py
from rest_framework import permissions
class IsOwnerOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
if request.user == obj.author:
return True
return False
class IsOwnerOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.user == obj.user :
return True
return False
Here is my code in views.py:
from requests import request
from rest_framework.viewsets import ModelViewSet
from rest_framework import viewsets, status, permissions
from django.http import Http404, JsonResponse
from rest_framework.views import Response
from .models import Tender, Input , Investor
from .serializers import TenderViewSerializer, InputViewSerializer, InvestViewSerializer
from user.models import User
from user.permissions import IsOwnerOnly, IsOwnerOrReadOnly
# Create your views here.
class TenderViewSet(viewsets.ModelViewSet):
serializer_class = TenderViewSerializer
permission_classes = (permissions.IsAuthenticated,)
def get_queryset(self):
if self.request.user.is_tender_holder:
queryset = Tender.objects.all()
return queryset
else:
return Response({"NO_ACCESS": "Access Denied"}, status=401)
def create(self, request, *args, **kwargs):
if self.request.user.is_tender_holder:
new_tender = Tender.objects.create(
name=request.data["name"],
description=request.data["description"],
date_due=request.data["date_due"],
location=request.data["location"],
contact=request.data['contact']
)
new_tender.save()
serializer = TenderViewSerializer(new_tender)
return Response(serializer.data)
else:
return Response({"NO_ACCESS": "Access Denied"}, status=401)
def destroy(self, request, *args, **kwargs):
if self.request.user.is_tender_holder:
try:
instance =self.get_object()
self.perform_destroy(instance)
except Http404:
pass
return Response({"message": "Tender deleted successfully"})
else:
return Response({"NO_ACCESS": "Access Denied"}, status=401)
# def update(self, request, *args, **kwargs):
class InvestorViewSet(ModelViewSet):
serializer_class = InvestViewSerializer
permission_classes = (permissions.IsAuthenticated,)
#queryset = Investor.objects.all()
def get_queryset(self):
if self.request.user.is_investor:
queryset = Investor.objects.all()
return queryset
else:
return Response({"NO_ACCESS": "Access Denied"}, status=401)
def create(self, request, *args, **kwargs):
if self.request.user.is_investor:
new_investment = Investor.objects.create(
name=request.data["name"],
description=request.data["description"],
location=request.data["location"],
contact=request.data['contact']
)
new_investment.save()
serializer = InvestViewSerializer(new_investment)
return Response(serializer.data)
else:
return Response({"NO_ACCESS": "Access Denied"}, status=401)
def destroy(self, request, *args, **kwargs):
if self.request.user.is_investor:
try:
instance =self.get_object()
self.perform_destroy(instance)
except Http404:
pass
return Response({"message": "Investment deleted successfully"})
else:
return Response({"NO_ACCESS": "Access Denied"}, status=401)
class InputViewSet(ModelViewSet):
serializer_class = InputViewSerializer
permission_classes = (permissions.IsAuthenticated, IsOwnerOrReadOnly)
#queryset = Input.objects.all()
def get_queryset(self, **kwargs):
if self.request.user.is_input_holder or self.request.user.is_superuser:
user = self.request.user
return Input.objects.filter(id=user.id)
else:
return Response({"NO_ACCESS": "Access Denied"}, status=401)
def create(self, request, *args, **kwargs):
if self.request.user.is_input_holder or self.request.user.is_superuser:
new_input = Input.objects.create(
name=request.data["name"],
description=request.data["description"],
price=request.data["price"],
quantity=request.data["quantity"],
contact=request.data['contact']
)
new_input.save()
serializer = InputViewSerializer(new_input)
return Response(serializer.data)
else:
return Response({"NO_ACCESS": "Access Denied"}, status=401)
def destroy(self, request, *args, **kwargs):
if self.request.user.is_input_holder or self.request.user.is_superuser:
try:
instance =self.get_object()
self.perform_destroy(instance)
except Http404:
pass
return Response({"message": "Input deleted successfully"})
else:
return Response({"NO_ACCESS": "Access Denied"}, status=401)
def has_object_permission(self, request, view, obj):
# Read permissions are allowed to any request,
# so we'll always allow GET, HEAD or OPTIONS requests.
if request.method in permissions.SAFE_METHODS:
return True
# Instance must have an attribute named `owner`.
return obj.owner == request.user
This code give view permission to all users
If you want to restrict all permission you should try something like this
def has_object_permission(self, request, view, obj):
# Instance must have an attribute named `owner`.
return obj.owner == request.user
My goal is to specify behavior that allows me to enter an ID into my URL.
Currently, I am able to send GET, PUT, and PATCH requests to the URL 'localhost:8000/api/players/?id=#' where the hash represents player id
I would like to update my code to understand that if I send a GET/PUT/PATCH request to 'localhost:8000/api/players/2' that I am looking for a player with id=2
my models.py
class player(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
team = models.CharField(max_length=50)
position = models.CharField(max_length=50)
number = models.IntegerField()
def __str__(self):
return "{}, {}".format(self.first_name, self.last_name)
my serializer.py
class playerSerializer(serializers.ModelSerializer):
class Meta:
model = player
fields = ['id', 'first_name', 'last_name', 'team', 'position', 'number']
my views.py
from django.shortcuts import render
from django.http import HttpResponse, response
from django.shortcuts import get_object_or_404
from rest_framework import serializers
from rest_framework. serializers import Serializer
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from main.models import player
from .serializers import playerSerializer
import json
class playerList(APIView):
serializer_class = playerSerializer
throttle_scope = "main_app"
def get_queryset(self, *args, **kwargs):
players = player.objects.all()
return players
def get(self, request, *args, **kwargs):
try:
id = request.query_params["id"]
if id != None:
player_object = player.objects.get(id=id)
serializer = playerSerializer(player_object)
except:
players = self.get_queryset()
serializer = playerSerializer(players, many=True)
return Response(serializer.data)
def post(self, request):
player_data = request.data
new_player = player.objects.create(
first_name=player_data['first_name'],
last_name=player_data['last_name'],
team=player_data['team'],
position=player_data['position'],
number=player_data['number']
)
new_player.save()
serializer = playerSerializer(new_player)
return Response(serializer.data)
def put(self, request, *args, **kwargs):
id = request.query_params["id"]
player_object = player.objects.get(id=id)
data = request.data
player_object.first_name = data["first_name"]
player_object.last_name = data["last_name"]
player_object.team = data["team"]
player_object.position = data["position"]
player_object.number = data["number"]
player_object.save()
serializer = playerSerializer(player_object)
return Response(serializer.data)
def patch(self, request, *args, **kwargs):
id = request.query_params["id"]
player_object = player.objects.get(id=id)
data = request.data
player_object.first_name = data.get('first_name', player_object.first_name)
player_object.last_name = data.get('last_name', player_object.last_name)
player_object.team = data.get('team', player_object.team)
player_object.position = data.get('position', player_object.position)
player_object.number = data.get('number', player_object.number)
player_object.save()
serializer = playerSerializer(player_object)
return Response(serializer.data)
my urls.py
urlpatterns = [
url('players', playerList.as_view()),
]
I have been stuck on this problem for a few days... any guidance would be greatly appreciated.
Thanks,
Austin H
views.py
class playerList(APIView):
serializer_class = playerSerializer
throttle_scope = "main_app"
def get(self, request, *args, **kwargs):
try:
# id = request.query_params["id"]
id = self.kwargs["id"]
if id != None:
player_object = player.objects.get(id=id)
serializer = playerSerializer(player_object)
except:
players = self.get_queryset()
serializer = playerSerializer(players, many=True)
return Response(serializer.data)
def post(self, request):
player_data = request.data
new_player = player.objects.create(
first_name=player_data['first_name'],
last_name=player_data['last_name'],
team=player_data['team'],
position=player_data['position'],
number=player_data['number']
)
new_player.save()
serializer = playerSerializer(new_player)
return Response(serializer.data)
def put(self, request, *args, **kwargs):
#id = request.query_params["id"]
id = self.kwargs["id"]
player_object = player.objects.get(id=id)
data = request.data
player_object.first_name = data["first_name"]
player_object.last_name = data["last_name"]
player_object.team = data["team"]
player_object.position = data["position"]
player_object.number = data["number"]
player_object.save()
serializer = playerSerializer(player_object)
return Response(serializer.data)
def patch(self, request, *args, **kwargs):
#id = request.query_params["id"]
id = self.kwargs["id"]
player_object = player.objects.get(id=id)
data = request.data
player_object.first_name = data.get('first_name', player_object.first_name)
player_object.last_name = data.get('last_name', player_object.last_name)
player_object.team = data.get('team', player_object.team)
player_object.position = data.get('position', player_object.position)
player_object.number = data.get('number', player_object.number)
player_object.save()
serializer = playerSerializer(player_object)
return Response(serializer.data)
urls.py
urlpatterns = [
url('players/<int:id>/', playerList.as_view()),
]
I would highly suggest using DRF's viewsets as it will cover most of the things that you need here with little code. But if you want to play around it yourself with your current view:
First you have to define another url that accepts the player id, while keeping the old url to preserve the current behavior your want:
urlpatterns = [
url('players/<int:pk>', playerList.as_view()),
url('players', playerList.as_view()),
]
players/<int:pk> will support urls like players/1 and pass 1 as a keyword argument pk in the view.
You can then change your view to something like this:
from django.shortcuts import get_object_or_404
class playerList(APIView):
...
def get_object(self, id)
return get_object_or_404(self.get_queryset(), id=id)
def get(self, request, pk=None, *args, **kwargs):
id = pk or request.query_params.get('id')
if id:
serializer = playerSerializer(self.get_object(id))
else:
serializer = playerSerializer(self.get_queryset(), many=True)
return Response(serializer.data)
def post(self, request, *args, **kwargs):
...
def put(self, request, pk=None, *args, **kwargs):
player_object = self.get_object(pk or request.query_params.get('id'))
...
def patch(self, request, pk=None, *args, **kwargs):
player_object = self.get_object(pk or request.query_params.get('id'))
...
id = pk or request.query_params.get('id')
This means that if pk was not passed (i.e. the url used was not players/1) then the view will just do the old behavior you have by getting the id from request.query_params. This is also why pk=None is set, in case we get the id from the query params. Also note that post needs to also capture args and kwargs.
I want to count the average of ratings ( in Reviews model ) and send it to my API.
Models.py
from django.db import models
from adminuser.models import Categories
from accounts.models import UserAccount as User
from django.core.validators import MaxValueValidator, MinValueValidator
# Create your models here.
class Gigs(models.Model):
title = models.CharField(max_length=255)
category = models.ForeignKey(Categories , on_delete=models.CASCADE)
price = models.DecimalField(max_digits=6, decimal_places=2)
details = models.TextField()
seller = models.ForeignKey(User,default=None, on_delete=models.CASCADE)
class Reviews(models.Model):
rating = models.SmallIntegerField( default=0,validators=[MaxValueValidator(5),MinValueValidator(1)])
comment = models.CharField(max_length=500)
item = models.ForeignKey(Gigs , on_delete=models.CASCADE)
buyer = models.ForeignKey(User ,default=None, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
Views.py
from django.shortcuts import render
from .models import Gigs,Reviews
from .serializers import GigsSerializer,ReviewsSerializer
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import ListModelMixin, CreateModelMixin , RetrieveModelMixin , DestroyModelMixin, UpdateModelMixin
from rest_framework.permissions import AllowAny
# Create your views here.
#List and create (pk not required)
class GigsListAPI(GenericAPIView, ListModelMixin ):
def get_queryset(self):
username = self.kwargs['user']
return Gigs.objects.filter(seller=username)
serializer_class = GigsSerializer
permission_classes = (AllowAny,)
def get(self, request , *args, **kwargs):
return self.list(request, *args, **kwargs)
class GigsListCategorywise(GenericAPIView, ListModelMixin ):
def get_queryset(self):
SearchedCategory = self.kwargs['category']
return Gigs.objects.filter(category=SearchedCategory)
serializer_class = GigsSerializer
permission_classes = (AllowAny,)
def get(self, request , *args, **kwargs):
return self.list(request, *args, **kwargs)
class GigsListAll(GenericAPIView, ListModelMixin ):
queryset = Gigs.objects.all()
serializer_class = GigsSerializer
permission_classes = (AllowAny,)
def get(self, request , *args, **kwargs):
return self.list(request, *args, **kwargs)
class GigsCreateAPI(GenericAPIView, CreateModelMixin):
queryset = Gigs.objects.all()
serializer_class = GigsSerializer
permission_classes = (AllowAny,)
def post(self, request , *args, **kwargs):
return self.create(request, *args, **kwargs)
# Retrieve, update and delete (pk required)
class RUDGigsAPI(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
queryset = Gigs.objects.all()
serializer_class = GigsSerializer
permission_classes = (AllowAny,)
def get(self, request , *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def put(self, request , *args, **kwargs):
return self.update(request, *args, **kwargs)
def put(self, request , *args, **kwargs):
return self.partial_update(request, *args, **kwargs)
def delete(self, request , *args, **kwargs):
pk = kwargs.get('pk')
p = Gigs.objects.get(id=pk)
if p.images:
p.images.delete()
return self.destroy(request, *args, **kwargs)
# VIEWS FOR REVIEWS MODEL
class ReviewsListAPI(GenericAPIView, ListModelMixin ):
def get_queryset(self):
item = self.kwargs['item']
return Reviews.objects.filter(item=item)
serializer_class = ReviewsSerializer
permission_classes = (AllowAny,)
def get(self, request , *args, **kwargs):
return self.list(request, *args, **kwargs)
class ReviewsCreateAPI(GenericAPIView, CreateModelMixin):
queryset = Reviews.objects.all()
serializer_class = ReviewsSerializer
permission_classes = (AllowAny,)
def post(self, request , *args, **kwargs):
return self.create(request, *args, **kwargs)
Serializers.py
from rest_framework import serializers
from .models import Gigs, Reviews
class GigsSerializer (serializers.ModelSerializer):
class Meta:
model = Gigs
fields = ['id','title','category','price','details','seller','images']
class ReviewsSerializer (serializers.ModelSerializer):
class Meta:
model = Reviews
fields = ['id','rating','comment','item','buyer','created_at']
I want to calculate average of the ratings of some gigs or item in reviews table and then send it to API. but I am confused where to calculate it (models.py or views.py) and then how to send it to my API.
Well I am gonna explain this in details, average rating could be considered as a virtual field in Gigs, so it make sense to put it in there, so lets try that:
class Gigs(models.Model):
...
#property
def average_rating(self):
return self.reviews.aggregate(Avg('rating'))['rating_avg']
so when you gonna retrieve a single Gigs, this is good and everything, but the problem is if you need the average in the list api, this is gonna make alot of extra queries(1 for each Gig). in that case it is better to do it in bulk and in the view, so:
class GigsListAll(ListModelMixin, GenericAPIView): # you should put the mixin before the main class :D
serializer_class = GigsSerializer
permission_classes = (AllowAny,)
def get_queryset(self):
return Gigs.objects.all().annotate(_average_rating=Avg('reviews__rating') # pay attention, it was annotated as _average_rating
and now we gonna change the virtual field in the model, and check if we have it precalculated, so:
class Gigs(models.Model):
...
#property
def average_rating(self):
if hasattr(self, '_average_rating'):
return self._average_rating
return self.reviews.aggregate(Avg('rating'))
finally to use it in your serializer:
class GigsSerializer (serializers.ModelSerializer):
average_rating = serializers.SerializerMethodField()
def get_average_rating(self, obj):
return obj.average_rating
class Meta:
model = Gigs
fields = ['id','title','category','price','details','seller','images','average_rating']
p.s. It is a best practice to set the related name for foreign keys, so change your reviews model like this:
class Reviews(models.Model):
...
item = models.ForeignKey(Gigs , on_delete=models.CASCADE, related_name='reviews')
First give your foreign key a name so you can reverse it:
class Reviews(models.Model):
...
item = models.ForeignKey(Gigs, on_delete=models.CASCADE, related_name='reviews')
...
Then you can do this in your serializer:
from rest_framework import serializers
from .models import Gigs, Reviews
from django.db.models import Avg
class GigsSerializer (serializers.ModelSerializer):
class Meta:
model = Gigs
fields = ['id','title','category','price','details','seller','images','avg_rating']
avg_rating = serializers.SerializerMethodField()
def get_avg_rating(self, ob):
# reverse lookup on Reviews using item field
return ob.reviews.all().aggregate(Avg('rating'))['rating__avg']
class ReviewsSerializer (serializers.ModelSerializer):
class Meta:
model = Reviews
fields = ['id','rating','comment','item','buyer','created_at']
It's redundant but I post it also here.
Objects properties have to be serialized using SerializerMethodField.
To get values for these type of fields serializers look for methodsnamed get_ and raises errors if they are not defined. The get_ method must accept an object as a parameter that the serializer use to pass in the current serialized object, so you can access its properties.
In this case it have to be:
class GigsSerializer(serializers.MethodSerializer):
average_rating = serializers.SerializerMethodField()
def get_average_rating(self, obj):
return obj.average_rating
class Meta:
model = Gigs
fields = ["""your fields here"""]
I'm creating a basic notes application where user can perform CRUD operation.
Following are my models, views and URLs.
from django.db import models
from accounts.models import User
from django.utils import timezone
class Notes(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
body = models.TextField()
created_on = models.DateTimeField(auto_now_add=True, editable=False)
last_updated = models.DateTimeField(auto_now=True)
def save(self, *args, **kwargs):
if not self.id:
self.created_on = timezone.now()
self.last_updated = timezone.now()
def __str__(self):
return self.title
Views.py
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework import viewsets, status
from rest_framework.response import Response
from . import serializers, models
class NotesViewSet(viewsets.ModelViewSet):
queryset = models.Notes.objects.all()
serializer_class = serializers.NotesSerializer
authentication_classes = (TokenAuthentication,)
permission_classes = (IsAuthenticated,)
def list(self, request):
notes = models.Notes.objects.all().filter(user=request.user)
serializer = serializers.NotesSerializer(notes, many=True)
return Response(serializer.data)
def create(self, request):
serializer = serializers.NotesSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
def retrieve(self, request, pk=None):
note = models.Notes.objects.filter(id=pk)
serializer = serializers.NotesSerializer(instance=note)
return Response(serializer.data)
def update(self, request, pk=None):
note = models.Notes.objects.get(id=pk)
serializer = serializers.NotesSerializer(
instance=note, data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_202_ACCEPTED)
def destroy(self, request, pk=None):
note = models.Notes.objects.get(id=pk)
note.delete()
return Response({"message": "Note deleted"},
status=status.HTTP_202_ACCEPTED)
urls.py
from django.urls import path
from .views import NotesViewSet
app_name = 'notes'
urlpatterns = [
path('get', NotesViewSet.as_view({
'get': 'list',
}), name='get'),
path('create', NotesViewSet.as_view({
'post': 'create',
}), name='create'),
path('get/<str:pk>', NotesViewSet.as_view({
'get': 'retrieve',
}), name='retrieve'),
path('update/<str:pk>', NotesViewSet.as_view({
'put': 'update',
}), name='update'),
path('delete/<str:pk>', NotesViewSet.as_view({
'delete': 'destroy',
}), name='delete'),
]
My question is, once I send a post request to create a new note something like this coming back as a response
{
"id": null,
"title": "title_1",
"body": "body_1",
"created_on": "2021-06-02T21:10:42.019236+05:30",
"last_updated": "2021-06-02T21:10:42.019257+05:30",
"user": 1
}
and the data isn't stored in the database as well. Please help
I believe the issue is in how you've overridden your save method- you still need to call the parent's save on it to get your expected behavior.
def save(self, *args, **kwargs):
if not self.id:
self.created_on = timezone.now()
self.last_updated = timezone.now()
super().save(*args, **kwargs)
Your code has some odd tabulation under your Notes class- I'm assuming this is just an issue in the code within the question.
super()
in save method is missing. You need to call super().save(*args, **kwargs) to make it work.
I need to add an image field and audio field in the front end. The Audio file should be accepted from a user in the front end. Then, ml file should be given the accepted file for processing and a text and image returned from it should be shown in the front end. How can I proceed?
models.py
from django.db import models
from django.conf import settings
class Question(models.Model):
document = models.FileField(upload_to='documents/',default= "../../audio.wav")
document = models.FileField('uploads/%Y/%m/% d/')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
author = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="questions")
def __str__(self):
return self.document
class Answer(models.Model):
transcript = models.TextField(max_length=255, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
question = models.ForeignKey(Question,
on_delete=models.CASCADE,
related_name="answers")
author = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
voters = models.ManyToManyField(settings.AUTH_USER_MODEL,
related_name="votes")
def __str__(self):
return self.transcript
view.py
from rest_framework import generics, status, viewsets
from rest_framework.exceptions import ValidationError
from rest_framework.generics import get_object_or_404
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
import speech_recognition as sr
import webbrowser as wb
from pydub import AudioSegment
from questions.api.permissions import IsAuthorOrReadOnly
from questions.api.serializers import AnswerSerializer, QuestionSerializer
from questions.models import Answer, Question
r1=sr.Recognizer()
class AnswerCreateAPIView(generics.CreateAPIView):
"""Allow users to answer a question instance if they haven't already."""
queryset = Answer.objects.all()
serializer_class = AnswerSerializer
permission_classes = [IsAuthenticated]
def perform_create(self, serializer):
request_user = self.request.user
kwarg_slug = self.kwargs.get("slug")
question = get_object_or_404(Question, slug=kwarg_slug)
if question.answers.filter(author=request_user).exists():
raise ValidationError("You have already answered this Question!")
serializer.save(author=request_user, question=question)
class AnswerLikeAPIView(APIView):
"""Allow users to add/remove a like to/from an answer instance."""
serializer_class = AnswerSerializer
permission_classes = [IsAuthenticated]
def delete(self, request, pk):
"""Remove request.user from the voters queryset of an answer instance."""
answer = get_object_or_404(Answer, pk=pk)
user = request.user
answer.voters.remove(user)
answer.save()
serializer_context = {"request": request}
serializer = self.serializer_class(answer, context=serializer_context)
return Response(serializer.data, status=status.HTTP_200_OK)
def post(self, request, pk):
"""Add request.user to the voters queryset of an answer instance."""
#audio_file = request.FILES.get('audio')
#shipingphoto_obj = ShipPhoto.
if request.FILES['audio']:
myfile = request.FILES['audio']
sound = AudioSegment.from_mp3(myfile)
sound.export("audio.wav", format="wav")
#half_point = len(sound)//6
with sr.AudioFile('audio.wav') as source:
audio = r1.listen(source)
print("Text"+r1.recognize_google(audio))
#fs = FileSystemStorage()
#filename = fs.save(myfile.name, myfile)
x= r1.recognize_google(audio)
#answer = get_object_or_404(Answer, pk=pk)
answer = get_object_or_404(Question, pk=pk)
user = request.user
x="Hello"
answer.transcript.add(x)
answer.voters.add(user)
answer.save()
serializer_context = {"request": request}
serializer = self.serializer_class(answer, context=serializer_context)
request.post
return Response(serializer.data, status=status.HTTP_200_OK)
class AnswerListAPIView(generics.ListAPIView):
"""Provide the answers queryset of a specific question instance."""
serializer_class = AnswerSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
kwarg_slug = self.kwargs.get("slug")
return Answer.objects.filter(question__slug=kwarg_slug).order_by("-created_at")
class AnswerRUDAPIView(generics.RetrieveUpdateDestroyAPIView):
"""Provide *RUD functionality for an answer instance to it's author."""
queryset = Answer.objects.all()
serializer_class = AnswerSerializer
permission_classes = [IsAuthenticated, IsAuthorOrReadOnly]
class QuestionViewSet(viewsets.ModelViewSet):
"""Provide CRUD +L functionality for Question."""
queryset = Question.objects.all().order_by("-created_at")
lookup_field = "slug"
serializer_class = QuestionSerializer
permission_classes = [IsAuthenticated, IsAuthorOrReadOnly]
def perform_create(self, serializer):
serializer.save(author=self.request.user)
Settings.py
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')