I've created a model using an abstract user class model class for Online flight ticket booking. I'm new to this so haven't added many functionalities to it.
I'm sharing my model.py, admin.py, serializer.py, view.py.
My question:
In views.py -> class UserRetrtieveUpdateDestroyAPIView(generics.RetrieveUpdateDestroyAPIView)
I want to give access for GET PUT DELETE for only ADMIN and USER who created this profile(owner). I'm using postman to check endpoints.
"Please do check my abstract user model if there is any mistake".
permission for "BookViewSet" and "BookingRetrtieveUpdateDestroyAPIView" I only want ADMIN and USER owner who created this booking to view or modify it.
I only want to get list of passengers associated by that particular user in "PassengerListCreateAPIView(generics.ListCreateAPIView):"
#models.py
import email
from pyexpat import model
from django.db import models
from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth.models import (
BaseUserManager, AbstractBaseUser
)
GENDER_CHOICES = (
(0, 'male'),
(1, 'female'),
(2, 'not specified'),)
class UserManager(BaseUserManager):
def create_user(self, email, name,contact_number,gender,address,state,city,country,pincode,dob ,password=None, password2=None):
if not email:
raise ValueError('User must have an email address')
user = self.model(
email=self.normalize_email(email),
name=name,
contact_number=contact_number,
gender=gender,
address=address,
state=state,
city=city,
country=country,
pincode=pincode,
dob=dob,
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, name,contact_number,gender,address,state,city,country,pincode,dob , password=None):
user = self.create_user(
email,
name=name,
contact_number=contact_number,
gender=gender,
address=address,
state=state,
city=city,
country=country,
pincode=pincode,
dob=dob,
password=password,
)
user.is_admin = True
user.save(using=self._db)
return user
class User(AbstractBaseUser):
email = models.EmailField(verbose_name='Email',max_length=255,unique=True)
name = models.CharField(max_length=200)
contact_number= models.IntegerField()
gender = models.IntegerField(choices=GENDER_CHOICES)
address= models.CharField(max_length=100)
state=models.CharField(max_length=100)
city=models.CharField(max_length=100)
country=models.CharField(max_length=100)
pincode= models.IntegerField()
dob = models.DateField()
# is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['name','contact_number','gender','address','state','city','country','pincode','dob']
def __str__(self):
return self.email
def has_perm(self, perm, obj=None):
"Does the user have a specific permission?"
# Simplest possible answer: Yes, always
return self.is_admin
def has_module_perms(self, app_label):
"Does the user have permissions to view the app `app_label`?"
# Simplest possible answer: Yes, always
return True
#property
def is_staff(self):
"Is the user a member of staff?"
# Simplest possible answer: All admins are staff
return self.is_admin
# Create your models here.
class Airport(models.Model):
Airport_name=models.CharField(max_length=100)
country=models.CharField(max_length=100)
def __str__(self):
return self.Airport_name
class Flight(models.Model):
flight_number = models.CharField(max_length=100,unique=True)
depart_date_time = models.DateTimeField(auto_now_add=True)
arrival_date_time = models.DateTimeField(auto_now_add=True)
origin = models.CharField(max_length=100, blank=True, default='')
destination = models.CharField(max_length=100, blank=True, default='')
price = models.IntegerField()
airline_name = models.CharField(max_length=100, blank=True, default='')
total_seats = models.IntegerField()
available_seats = models.IntegerField()
airport=models.ForeignKey(Airport,on_delete=models.CASCADE)
def __str__(self):
return str(self.flight_number)
class Passenger(models.Model):
name = models.CharField(max_length=100,blank=True, default='')
contact_number= models.IntegerField()
email = models.EmailField(max_length=254)
gender = models.IntegerField(choices=GENDER_CHOICES)
age= models.IntegerField()
user=models.ForeignKey(User,on_delete=models.CASCADE)
def __str__(self):
return self.name
class Booking(models.Model):
user =models.ForeignKey(User,on_delete=models.CASCADE)
flights =models.ForeignKey(Flight,on_delete=models.CASCADE)
passenger =models.ManyToManyField(Passenger)
booking_number= models.CharField(max_length= 100,default=0, blank= True)
booking_time = models.DateTimeField(auto_now_add=True)
no_of_passengers= models.IntegerField(default=0, blank= True)
def __str__(self):
return self.booking_number
Corresponding serializer
#serializers.py
from rest_framework import serializers
from myapp.models import Airport, Flight, User, Passenger, Booking
class UserRegistrationSerializer(serializers.ModelSerializer):
# We are writing this becoz we need confirm password field in our Registratin Request
password2 = serializers.CharField(style={'input_type':'password'}, write_only=True)
class Meta:
model = User
fields=['email','name','contact_number','gender','address','state','city','country','pincode','dob','password', 'password2']
extra_kwargs={
'password':{'write_only':True}
}
# Validating Password and Confirm Password while Registration
def validate(self, attrs):
password = attrs.get('password')
password2 = attrs.get('password2')
if password != password2:
raise serializers.ValidationError("Password and Confirm Password doesn't match")
return attrs
def create(self, validate_data):
return User.objects.create_user(**validate_data)
class UserLoginSerializer(serializers.ModelSerializer):
email = serializers.EmailField(max_length=255)
class Meta:
model = User
fields = ['email', 'password']
# class UserProfileSerializer(serializers.ModelSerializer):
# class Meta:
# model = User
# fields = '__all__'
class AirportSerializer(serializers.ModelSerializer):
class Meta:
model = Airport
fields = '__all__'
class FlightSerializer(serializers.ModelSerializer):
class Meta:
model = Flight
fields = '__all__'
class UserSerializer(serializers.ModelSerializer):
class Meta:
model= User
fields = '__all__'
class PassengerSerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=False)
class Meta:
model= Passenger
fields = '__all__'
class BookingSerializer(serializers.ModelSerializer):
class Meta:
model= Booking
fields = '__all__'
#views.py
from django.shortcuts import render
from django.http import Http404, JsonResponse
#from django.http import HttpResponse, JsonResponse
from rest_framework.views import APIView
from rest_framework.response import Response
#from rest_framework.parsers import JSONParser
from django.contrib.auth import authenticate
from rest_framework_simplejwt.tokens import RefreshToken
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import SAFE_METHODS, BasePermission,IsAuthenticatedOrReadOnly,IsAuthenticated, IsAdminUser, DjangoModelPermissionsOrAnonReadOnly, DjangoModelPermissions
from myapp.renderers import UserRenderer
from rest_framework import status
from rest_framework import permissions
from rest_framework import generics
from myapp.models import Airport, Flight, User, Passenger, Booking
from myapp.serializers import *
from myapp.permissions import IsOwnerOrAdmin
from rest_framework import viewsets
def get_tokens_for_user(user):
refresh = RefreshToken.for_user(user)
return {
'refresh': str(refresh),
'access': str(refresh.access_token),
}
# Create your views here.
class UserRegistrationView(APIView):
renderer_classes = [UserRenderer]
def post(self, request, format=None):
serializer = UserRegistrationSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.save()
token = get_tokens_for_user(user)
return Response({'token':token, 'msg':'Registration Successful'}, status=status.HTTP_201_CREATED)
class UserLoginView(APIView):
renderer_classes = [UserRenderer]
def post(self, request, format=None):
serializer = UserLoginSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
email = serializer.data.get('email')
password = serializer.data.get('password')
user = authenticate(email=email, password=password)
if user is not None:
token = get_tokens_for_user(user)
return Response({'token':token, 'msg':'Login Success'}, status=status.HTTP_200_OK)
else:
return Response({'errors':{'non_field_errors':['Email or Password is not Valid']}}, status=status.HTTP_404_NOT_FOUND)
class UserProfileView(APIView):
renderer_classes = [UserRenderer]
permission_classes = [IsAuthenticated]
def get(self, request, format=None):
serializer = UserSerializer(request.user)
return Response(serializer.data, status=status.HTTP_200_OK)
class UserListCreateAPIView(generics.ListCreateAPIView):
permission_classes = [IsUserOrIsAdmin]
queryset = User.objects.all()
serializer_class = UserSerializer
class UserRetrtieveUpdateDestroyAPIView(generics.RetrieveUpdateDestroyAPIView):
permission_classes = [IsUserOrIsAdmin]
queryset = User.objects.all()
serializer_class = UserSerializer
class FlightListCreateAPIView(generics.ListCreateAPIView):
permission_classes = [IsAdminUser | IsAuthenticatedOrReadOnly]
queryset = Flight.objects.all()
serializer_class = FlightSerializer
class FlightRetrtieveUpdateDestroyAPIView(generics.RetrieveUpdateDestroyAPIView):
permission_classes = [IsAdminUser]
queryset = Flight.objects.all()
serializer_class = FlightSerializer
class AirportListCreateAPIView(generics.ListCreateAPIView):
permission_classes = [IsAdminUser | IsAuthenticatedOrReadOnly]
queryset = Airport.objects.all()
serializer_class = AirportSerializer
class AirportRetrtieveUpdateDestroyAPIView(generics.RetrieveUpdateDestroyAPIView):
permission_classes = [IsAdminUser]
queryset = Airport.objects.all()
serializer_class = AirportSerializer
class PassengerListCreateAPIView(generics.ListCreateAPIView):
permission_classes = [IsUserOrIsAdminPassenger]
queryset = Passenger.objects.all()
serializer_class = PassengerSerializer
class PassengerRetrtieveUpdateDestroyAPIView(generics.RetrieveUpdateDestroyAPIView):
permission_classes = [IsUserOrIsAdminPassenger]
queryset = Passenger.objects.all()
serializer_class = PassengerSerializer
class BookingRetrtieveUpdateDestroyAPIView(generics.RetrieveUpdateDestroyAPIView):
permission_classes = [IsUserOrIsAdmin]
# queryset = Booking.objects.all()
# serializer_class = BookingSerializer
def get_queryset(self):
if self.request.user.is_staff == False:
user_data= self.request.user
book = Booking.objects.filter(user= user_data)
return book
else:
book = Booking.objects.all()
return book
serializer_class = BookingSerializer
class BookViewSet(viewsets.ModelViewSet):
permission_classes = [IsUserOrIsAdmin]
# queryset = Book.objects.all()
serializer_class = BookingSerializer
# print(serializer_class)
def get_queryset(self):
if self.request.user.is_staff == False:
user_data= self.request.user
book = Booking.objects.filter(user= user_data)
return book
else:
book = Booking.objects.all()
return book
# book = Booking.objects.all()
def create(self, request, *args, **kwargs):
data = request.data
user = User.objects.get(id= request.user.pk)
# user = User.objects.get(id=data["user"])
flightdetails = Flight.objects.get(id=data["flights"])
# bookingdetails = Booking.objects.get(no_of_passengers=data["no_of_passengers"])
new_book = Booking.objects.create(
booking_number= data["booking_number"],
no_of_passengers= data["no_of_passengers"],
user=user,
flights=flightdetails,
)
new_book.save()
for passenger in data["passenger"]:
passenger_book= Passenger.objects.create(
user = user,
name= passenger["name"],
contact_number = passenger["contact_number"],
email = passenger["email"],
gender = passenger["gender"],
age = passenger["age"]
)
new_book.passenger.add(passenger_book)
if flightdetails.available_seats < len(data["passenger"]):
return Response({"data": "No seats available", "status": status.HTTP_400_BAD_REQUEST})
update_seats = flightdetails.available_seats - data["no_of_passengers"]
flightdetails.available_seats = update_seats
flightdetails.save()
serializers = BookingSerializer(new_book)
return Response({"data": serializers.data, "status": status.HTTP_201_CREATED})
#urls.py
from django.urls import path, include
from myapp.views import *
from rest_framework import routers
router = routers.DefaultRouter()
router.register('booking', BookViewSet, basename='MyModel')
urlpatterns = [
path('register/', UserRegistrationView.as_view(), name='register'),
path('login/', UserLoginView.as_view(), name='login'),
path('profile/', UserProfileView.as_view(), name='profile'),
path('flight/', FlightListCreateAPIView.as_view()),
path('flight_info/<int:pk>/', FlightRetrtieveUpdateDestroyAPIView.as_view()),
path('customer/', UserListCreateAPIView.as_view()),
path('customer_info/<int:pk>/', UserRetrtieveUpdateDestroyAPIView.as_view()),
path('passenger/', PassengerListCreateAPIView.as_view()),
path('passenger_info/<int:pk>/', PassengerRetrtieveUpdateDestroyAPIView.as_view()),
path('booking_info/<int:pk>/', BookingRetrtieveUpdateDestroyAPIView.as_view()),
#path('booking/', BookingAPIView.as_view()),
path('', include(router.urls)),
]
You'll need to create a custom permission for this. Since the permission is dependent on the User instance, you'll want to implement has_object_permission(), returning True if the user is_staff or if the user is the same as the User they're trying to access:
from django.contrib.auth import get_user_model
from rest_framework.permissions import BasePermission
User = get_user_model()
class IsUserOrIsAdmin(BasePermission):
"""Allow access to the respective User object and to admin users."""
def has_object_permission(self, request, view, obj):
return (request.user and request.user.is_staff) or (
isinstance(obj, User) and request.user == obj
)
You can then set your view's permission_classes to your custom permission:
class UserRetrtieveUpdateDestroyAPIView(generics.RetrieveUpdateDestroyAPIView):
permission_classes = [IsUserOrIsAdmin]
queryset = User.objects.all()
serializer_class = UserSerializer
Related
here i am trying to add custom permissions
1.user can rate the movie once
2.Users can add a movie and other people, except the creator, can rate it.
i have written the custom permission class in the permission.py but still it not doing what i want
but it is going wrong .can please some help one
models.py
from django.contrib.auth.models import User
from django.core.validators import MinValueValidator, MaxValueValidator
class Movie(models.Model):
title = models.CharField(max_length=128)
director = models.CharField(max_length=128)
added_by = models.ForeignKey(User, related_name="movie", on_delete=models.CASCADE, null=True)
added_at = models.DateTimeField(auto_now_add=True)
# rating=models.IntegerField()
class Meta:
db_table = "Movie"
class Rating(models.Model):
movie = models.ForeignKey(Movie, on_delete=models.CASCADE)
rating = models.IntegerField(validators=[MinValueValidator(0),MaxValueValidator(5)])
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='user_rating')
class Meta:
db_table = "Rating"
views.py
from rest_framework.response import Response
from rest_framework.decorators import permission_classes
from rest_framework.permissions import IsAuthenticated
from knox.models import AuthToken
from TestApp.models import Movie, Rating
from TestApp.serializer import UserSerializer, RegisterSerializer, LoginSerializer, MovieSerializer, RatingSerializer
from TestApp.permissions import *
class UserAPIView(generics.RetrieveAPIView):
permission_classes = [
permissions.IsAuthenticated,
]
serializer_class = UserSerializer
def get_object(self):
return self.request.user
class RegisterAPIView(generics.GenericAPIView):
serializer_class = RegisterSerializer
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.save()
return Response({
"user": UserSerializer(user, context=self.get_serializer_context()).data,
"token": AuthToken.objects.create(user)[1]
})
class LoginAPIView(generics.GenericAPIView):
serializer_class = LoginSerializer
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data
return Response({
"user": UserSerializer(user, context=self.get_serializer_context()).data,
"token": AuthToken.objects.create(user)[1]
})
class MovieAPIView(generics.ListCreateAPIView):
serializer_class = MovieSerializer
def get_queryset(self):
return Movie.objects.all()
#permission_classes([IsAuthenticated])
def perform_create(self, serializer):
serializer.save(added_by=self.request.user)
class RatingAPIView(generics.CreateAPIView):
serializer_class = RatingSerializer
#permission_classes([IsAuthenticated,UserPermission])
def perform_create(self, serializer):
serializer.save(user=self.request.user)
serializers.py
from django.contrib.auth import authenticate
from django.contrib.auth.models import User
from rest_framework import serializers
from TestApp.models import Movie, Rating
from urllib import request
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'email')
class RegisterSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'email', 'password')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
user = User.objects.create_user(validated_data['username'], validated_data['email'], validated_data['password'])
return user
class LoginSerializer(serializers.Serializer):
username = serializers.CharField()
password = serializers.CharField()
def validate(self, data):
user = authenticate(**data)
if user and user.is_active:
return user
raise serializers.ValidationError("Wrong Credentials")
class MovieSerializer(serializers.ModelSerializer):
class Meta:
model = Movie
fields = ['id', 'title', 'director']
class RatingSerializer(serializers.ModelSerializer):
# id=MovieSerializer(read_only=True)
class Meta:
model = Rating
fields = ['id', 'movie','rating','user']
permissions.py
from TestApp.models import *
class UserPermission(BasePermission):
message = 'Adding ratings by same user not allowed.'
def has_permission(self, request, view,attrs):
allowed_methods = ['POST', 'PATCH']
#validated_data = super().validate(attrs)
user = self.context['request'].user
if request.method in SAFE_METHODS and Rating.objects.filter(movie=validated_data['movie'], user=user).exists():
return True
else:
return False
class Add(BasePermission):
def has_permission(self, request, view):
allowed_methods = ['POST', 'PATCH']
user = Movie.objects.get(id=user_id)
t = Rating.objects.filter(added_by_id=user)
if request.method in allowed_methods:
if t == user:
return False
else:
return True
else:
return True
def has_permission(self, request, view,attrs):
allowed_methods = ['POST', 'PATCH']
#validated_data = super().validate(attrs)
user = self.context['request'].user
ratings = Rating.objects.filter(movie=validated_data['movie'], user=user)
if validated_data['movie'].added_by == user:
return False
elif request.method in SAFE_METHODS and not ratings.exists():
return True
else:
return False
If you dont want to allow user to rate twice then user not Rating.objects.filter().exists() i.e if object doesn’t not exists then return True
here i want to join the two tables and i want that once the user is logged in then he/she can rate a movie and only one user can rate the movie and if the same user tries to rate it again then it should show an error message
i have used knox for 3rd party authentication and i want to join the movie and rating table
models.py
from django.db import models
from django.contrib.auth.models import User
from django.core.validators import MinValueValidator, MaxValueValidator
class Movie(models.Model):
title = models.CharField(max_length=128)
director = models.CharField(max_length=128)
added_by = models.ForeignKey(User, related_name="movies", on_delete=models.CASCADE, null=True)
added_at = models.DateTimeField(auto_now_add=True)
# rating=models.IntegerField()
class Meta:
db_table = "Movie"
class Rating(models.Model):
movies=models.CharField(max_length=128)
rating = models.IntegerField(validators=[MinValueValidator(0),
MaxValueValidator(5)])
class Meta:
db_table = "Rating"
Views.py
from rest_framework.response import Response
from rest_framework.decorators import permission_classes
from rest_framework.permissions import IsAuthenticated
from knox.models import AuthToken
from TestApp.models import Movie, Rating
from TestApp.serializer import UserSerializer, RegisterSerializer, LoginSerializer, MovieSerializer, RatingSerializer
from django.shortcuts import render
# , RegisterSerializer, LoginSerializer, MovieSerializer
class UserAPIView(generics.RetrieveAPIView):
permission_classes = [
permissions.IsAuthenticated,
]
serializer_class = UserSerializer
def get_object(self):
return self.request.user
class RegisterAPIView(generics.GenericAPIView):
serializer_class = RegisterSerializer
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.save()
return Response({
"user": UserSerializer(user, context=self.get_serializer_context()).data,
"token": AuthToken.objects.create(user)[1]
})
class LoginAPIView(generics.GenericAPIView):
serializer_class = LoginSerializer
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data
return Response({
"user": UserSerializer(user, context=self.get_serializer_context()).data,
"token": AuthToken.objects.create(user)[1]
})
class MovieAPIView(generics.ListCreateAPIView):
serializer_class = MovieSerializer
def get_queryset(self):
return Movie.objects.all()
#permission_classes([IsAuthenticated])
def perform_create(self, serializer):
serializer.save(added_by=self.request.user)
class RatingAPIView(generics.ListCreateAPIView):
serializer_class = RatingSerializer
def get_queryset(self):
return Rating.objects.all()
"""def create(self,serializer):
return Rating.objects.all()
user=self.request.user
if not user:
return ("Please Authenticate Yourself")
else:
return Response(Rating.objects.all().prefetch_related('movies'))"""
serializers.py
from django.contrib.auth.models import User
from rest_framework import serializers
from TestApp.models import Movie, Rating
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'email')
class RegisterSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'email', 'password')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
user = User.objects.create_user(validated_data['username'], validated_data['email'], validated_data['password'])
return user
class LoginSerializer(serializers.Serializer):
username = serializers.CharField()
password = serializers.CharField()
def validate(self, data):
user = authenticate(**data)
if user and user.is_active:
return user
raise serializers.ValidationError("Wrong Credentials")
class MovieSerializer(serializers.ModelSerializer):
class Meta:
model = Movie
fields = ['id', 'title', 'director']
class RatingSerializer(serializers.ModelSerializer):
#id=MovieSerializer(read_only=True)
class Meta:
model = Rating
fields = ['id','movies', 'rating']
please help how to perform join of the two table and how can i implement once user only one review
thanks in advance
In your Rating model you will have to add a ForeignKey to your User model. Also you need to make your movies field as a ForeignKey to link it to the Movies model
models.py
class Rating(models.Model):
movies=models.ForeignKey(Movies, on_delete=models.CASCADE)
rating = models.IntegerField(validators=[MinValueValidator(0),
MaxValueValidator(5)])
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='user_rating)
class Meta:
db_table = "Rating"
Now inside your serializer's validation method you can validate if the user has already rated the movie or not
serializes.py
class RatingSerializer(serializers.ModelSerializer):
def validate(self, attrs):
validated_data = super().validate(attrs)
user = self.context['request'].user
if Ratings.objects.filter(movies=validated_data['movies'],user=user).exists():
raise serializers.ValidationError('User already rated the movie')
return validated_data
class Meta:
model = Rating
fields = ['id','movies', 'user', 'rating']
Then to get the average rating for a movie you can do
from django.db.models import Avg
Ratings.objects.filter(movies__name='movie_name').aggregrate(Avg('rating'))
Django will take care of joining the tables and filtering the results
serializers.SerializerMethodField() is only meant to read value ie
retrieve, it will not update the lang value.
I want to create an API that can update a field value. Mainly, I want to change the field lang in the User model. Here is my code
models.py
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, related_name='profile', on_delete=models.CASCADE)
member_since = models.DateTimeField(default=timezone.now)
lang = models.CharField(max_length = 5, default = 'en')
def get_lang(self):
return self.lang
serializers.py
from django.contrib.auth import get_user_model
User = get_user_model()
class ProfileRetrieveUpdateSerializer(serializers.ModelSerializer):
member_since = serializers.SerializerMethodField()
lang = serializers.SerializerMethodField()
class Meta:
model = User
fields = [
'member_since','lang'
]
def get_member_since(self, obj):
return obj.profile.member_since.date()
def get_lang(self, obj):
return obj.profile.get_lang()
def update(self, instance, validated_data):
print(validated_data)
setattr(instance.profile, "lang", validated_data.get("lang",instance.profile.lang))
instance.save()
return instance
views.py
from django.contrib.auth import get_user_model
from .serializers import (
CurrentUserDetailSerializer, ProfileRetrieveUpdateSerializer,
UserLoginSerializer, UserSerializerWithToken,
)
User = get_user_model()
class ProfileRetrieveUpdateAPIView(RetrieveUpdateAPIView):
"""
View that returns user profile data.
"""
permission_classes = [AllowAny]
queryset = User.objects.all()
serializer_class = ProfileRetrieveUpdateSerializer
lookup_field = 'username'
lookup_url_kwarg = 'username'
axios.patch(url, {'lang': 'fr') (additional, I don't think it's wrong here)
When I call API, I get that the validated_data value is {}!
I think the problem is in the serializers.py module.
lang = serializers.CharField(source = 'get_lang')
def update(self, instance, validated_data):
setattr(instance.profile, "lang", validated_data.get("get_lang"))
instance.save()
return instance
reason: serializers.SerializerMethodField() is only meant to read value ie retrieve, it will not update lang value.
I'm trying to create an order by sending it as a POST request via my DRF API but I keep getting ""POST /shop/orders/ HTTP/1.1" 400 81" errors.
The error says "{"total_price":["This field is required."],"puppies":["This field is required."]}" even though I'm supplying it.
The GET is working fine but I only have orders that I manually wrote in the DB.
Our project is a fictional webshop where you can put dog babies in your cart and order them. The three main models that we have are Users, Orders and Puppies. Because every order has a list of puppies and every puppy has its own amount we needed an intermediary model for the two as a simple ManyToManyField apparently can't handle extra columns. This makes things much more complicated (at least for someone who is new to Django).
I can't find anything on the internet for this special case. I have been trying to get this to work for almost a week now and I don't see what I'm doing wrong.
models.py:
from django.db import models
from django.conf import settings
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
class Puppy(models.Model):
name = models.CharField(max_length=30)
price = models.DecimalField(max_digits=6, decimal_places=2)
image_url = models.CharField(max_length=200)
age = models.IntegerField(null=True)
weight = models.IntegerField(null=True)
description_de = models.CharField(max_length=500, null=True)
description_en = models.CharField(max_length=500, null=True)
def __str__(self):
return self.name
class Order(models.Model):
total_price = models.DecimalField(max_digits=9, decimal_places=2)
puppies = models.ManyToManyField(Puppy, through='PuppyOrder')
date = models.DateTimeField(auto_now_add=True, blank=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='orders')
def __str__(self):
return str(self.id)
class PuppyOrder(models.Model):
order = models.ForeignKey(Order, on_delete=models.CASCADE)
puppy = models.ForeignKey(Puppy, on_delete=models.CASCADE)
amount = models.IntegerField()
def __str__(self):
return str(self.id)
class Address(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
country = models.CharField(max_length=30, blank=True)
street = models.CharField(max_length=30, blank=True)
zip = models.CharField(max_length=10, blank=True)
city = models.CharField(max_length=30, blank=True)
#receiver(post_save, sender=User)
def create_user_address(sender, instance, created, **kwargs):
if created:
Address.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_address(sender, instance, **kwargs):
instance.address.save()
Fetch in react:
async function createOrder(order) {
order = order.map(element =>
element = {'puppy' : element.puppy.id, 'amount': element.amount}
)
let orderReq = {"total_price": "500.00", "puppies": order}
console.log(JSON.stringify(orderReq))
const res = await fetch(REACT_APP_BACKEND_URL + `/shop/orders/`, {
method: 'POST',
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ orderReq })
})
return res.json()
}
Console.log results in: {"total_price":"500.00","puppies":[{"puppy":2,"amount":3},{"puppy":4,"amount":4}]}
"500.00" is just a placeholder value. Next step will be finding a place to calculate the total_price.
views.py:
from shop.models import Puppy, Order
from django.contrib.auth.models import User
from rest_framework import permissions, status, viewsets, generics
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.reverse import reverse
from rest_framework.exceptions import ValidationError
from rest_framework_jwt.serializers import VerifyJSONWebTokenSerializer
from .serializers import UserSerializer, UserSerializerWithToken, OrderSerializer, PuppySerializer
#api_view(['GET'])
def api_root(request, format=None):
return Response({
'users': reverse('user-list', request=request, format=format),
'orders': reverse('order-list', request=request, format=format),
'puppies': reverse('puppy-list', request=request, format=format),
})
class OrderList(generics.ListCreateAPIView):
print("Test")
queryset = Order.objects.all()
permission_classes = (permissions.AllowAny,)
serializer_class = OrderSerializer
def get_queryset(self):
if self.request.user is None or self.request.user.is_anonymous:
self.request.user = get_request_user_and_validate_token(self)
return Order.objects.all().filter(user=self.request.user)
def perform_create(self, serializer):
print(self.request.data)
if self.request.user is None or self.request.user.is_anonymous:
self.reqest.user = get_request_user_and_validate_token(self)
if serializer.is_valid():
print('Creating new order...')
serializer.create(user=self.request.user, puppies=self.request.data)
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class OrderDetail(generics.RetrieveAPIView):
permission_classes = (permissions.AllowAny,)
queryset = Order.objects.all()
serializer_class = OrderSerializer
def get_queryset(self):
if self.request.user is None or self.request.user.is_anonymous:
self.request.user = get_request_user_and_validate_token(self)
return Order.objects.all().filter(user=self.request.user)
class UserList(generics.ListAPIView):
queryset = User.objects.all().select_related('address')
serializer_class = UserSerializer
class UserDetail(generics.RetrieveAPIView):
queryset = User.objects.all().select_related('address')
serializer_class = UserSerializer
class PuppyList(generics.ListAPIView):
permission_classes = (permissions.AllowAny,)
queryset = Puppy.objects.all()
serializer_class = PuppySerializer
class PuppyDetail(generics.RetrieveAPIView):
permission_classes = (permissions.AllowAny,)
queryset = Puppy.objects.all()
serializer_class = PuppySerializer
def get_request_user_and_validate_token(self):
print(self.request.user)
token = self.request.META.get('HTTP_AUTHORIZATION', " ").split(' ')[1]
data = {'token': token}
try:
valid_data = VerifyJSONWebTokenSerializer().validate(data)
print(valid_data['user'])
return valid_data['user']
except ValidationError as v:
print("validation error", v)
serializers.py
from rest_framework import serializers
from rest_framework_jwt.settings import api_settings
from django.contrib.auth.models import User
from django.db import models
from .models import Order, Puppy, PuppyOrder
class UserSerializer(serializers.ModelSerializer):
orders = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = User
fields = ('id', 'username', 'orders')
class UserSerializerWithToken(serializers.ModelSerializer):
# Code dealing with JWT token creation ...
class PuppySerializer(serializers.ModelSerializer):
description = serializers.SerializerMethodField()
def get_description(self, puppy):
return {'DE': puppy.description_de, 'EN': puppy.description_en}
class Meta:
model = Puppy
fields = ('id', 'name', 'price', 'image_url', 'age', 'weight', 'description')
class PuppyOrderSerializer(serializers.ModelSerializer):
puppy = serializers.ReadOnlyField(source='puppy.id')
class Meta:
model = PuppyOrder
fields = ('puppy', 'amount')
class OrderSerializer(serializers.ModelSerializer):
puppies = PuppyOrderSerializer(source='puppyorder_set', many=True)
user = serializers.ReadOnlyField(source='user.username')
total_price = "100.00"
class Meta:
model = Order
fields = ('id', 'total_price', 'puppies', 'date', 'user')
Any help would be much appreciated!
I am going through thinkster.io tutorial about Django and AngularJS. I am about half way of tutorial, but I am getting the error 405 Method not allowed for my POST request of user registration. I tried all sugestions but none of them seems to work ( I also tried to copying whole code from tutorial ). Here is my code:
views.py
from django.shortcuts import render
from rest_framework import permissions, viewsets
from authentication.models import Account
from authentication.permissions import IsAccountOwner
from authentication.serializers import AccountSerializer
class AccountViewSet(viewsets.ModelViewSet):
lookup_field = 'username'
queryset = Account.objects.all()
serializer_class = AccountSerializer
def get_permissions(self):
if self.request.method in permissions.SAFE_METHODS:
return (permissions.AllowAny(),)
if self.request.method == 'POST':
return (permissions.AllowAny(),)
return (permissions.IsAuthenticated(), IsAccountOwner(),)
def create(self, request):
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
Account.objects.create_user(**serializer.validated_data)
return Response(serializer.validated_data, status=status.HTTP_201_CREATED)
return Response({
'status': 'Bad request',
'message': 'Account could not be created with received data.'
}, status=status.HTTP_400_BAD_REQUEST)
serializers.py
from django.contrib.auth import update_session_auth_hash
from rest_framework import serializers
from authentication.models import Account
class AccountSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True, required=False)
confirm_password = serializers.CharField(write_only=True, required=False)
class Meta:
model = Account
fields = ('id', 'email', 'username', 'created_at', 'updated_at',
'first_name', 'last_name', 'tagline', 'password',
'confirm_password',)
read_only_fields = ('created_at', 'updated_at',)
def create(self, validated_data):
return Account.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.username = validated_data.get('username', instance.username)
instance.tagline = validated_data.get('tagline', instance.tagline)
instance.save()
password = validated_data.get('password', None)
confirm_password = validated_data.get('confirm_password', None)
if password and confirm_password and password == confirm_password:
instance.set_password(password)
instance.save()
update_session_auth_hash(self.context.get('request'), instance)
return instance
permissions.py
from rest_framework import permissions
class IsAccountOwner(permissions.BasePermission):
def has_object_permission(self, request, view, account):
if request.user:
return account == request.user
return False
urls.py
from django.conf.urls import patterns, url, include
from thinkster_django_angular_boilerplate.views import IndexView
from rest_framework_nested import routers
from authentication.views import AccountViewSet
router = routers.SimpleRouter()
router.register(r'accounts', AccountViewSet)
urlpatterns = patterns(
'',
# ... URLs
url(r'^/api/v1/', include(router.urls)),
url('^.*$', IndexView.as_view(), name='index'),
)
models.py
from django.contrib.auth.models import AbstractBaseUser
from django.contrib.auth.models import BaseUserManager
from django.db import models
class AccountManager(BaseUserManager):
def create_user(self, email, password=None, **kwargs):
if not email:
raise ValueError('Users must have a valid email address.')
if not kwargs.get('username'):
raise ValueError('Users must have a valid username.')
account = self.model(
email=self.normalize_email(email), username=kwargs.get('username')
)
account.set_password(password)
account.save()
return account
def create_superuser(self, email, password, **kwargs):
account = self.create_user(email, password, **kwargs)
account.is_admin = True
account.save()
return account
class Account(AbstractBaseUser):
email = models.EmailField(unique=True)
username = models.CharField(max_length=40, unique=True)
first_name = models.CharField(max_length=40, blank=True)
last_name = models.CharField(max_length=40, blank=True)
tagline = models.CharField(max_length=140, blank=True)
is_admin = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
objects = AccountManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
def __unicode__(self):
return self.email
def get_full_name(self):
return ' '.join([self.first_name, self.last_name])
def get_short_name(self):
return self.first_name
I think the problem is with Django code, because AngularJS seems to not throw any errors and I can see that request is coming to the django. I am using python3.4, Django 1.8.3, and Django-rest-framework 3.1.