http://127.0.0.1:8000/app_restFramework/users/ , return text
AttributeError at /app_restFramework/users/
'User' object has no attribute 'books'
models.py
class User(models.Model):
username = models.CharField(max_length=100)
class Book(models.Model):
name = models.CharField(max_length=100)
author = models.CharField(max_length=100)
publisher = models.CharField(max_length=100)
time = models.CharField(max_length=100)
owner = models.ManyToManyField(User)
serializers.py
from app_restFramework.models import Book,User
class UserSerializer(serializers.ModelSerializer):
books = serializers.PrimaryKeyRelatedField(many = True, read_only = True)
class Meta:
model = User
fields = ('id', 'username', 'books')
views.py
class UserList(generics.ListCreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
urls.py
url(r'^app_restFramework/users/$', app_restFramework.views.UserList.as_view() ),
You have not specified the related_name in the ManyToManyField. By default it will be book_set. Therefore you can do:
book_set = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
If you want to use books in the serializers, you can do this in the Book model:
owner = models.ManyToManyField(User, related_name="books")
Related
I am creating rest APIs for a website in which users can purchase one of the provided subscriptions.
In this website there is a user-info API which returns the information about the logged in user which can be used to show their info on the website.
The problem is that, the mentioned API's serializer is a modelSerializer on the "User" model and the information that I want to return is the instance of "Subscription" model which the latest instance of "SubPurchase" model refers to.
These are my serializers, models and views.And I need to somehow return the user's current subscription's ID and name along with the user's information. If you have any further questions, ask me in the comments and I'll answer them.
# models.py
class User(AbstractBaseUser, PermissionsMixin):
userID = models.AutoField(primary_key=True)
username = models.CharField(max_length=100, unique=True, validators=[RegexValidator(regex="^(?=[a-z0-9._]{5,20}$)(?!.*[_.]{2})[^_.].*[^_.]$")])
email= models.EmailField(max_length=100, unique=True, validators=[EmailValidator()])
name = models.CharField(max_length=100)
isSuspended = models.BooleanField(default=False)
isAdmin = models.BooleanField(default=False)
emailActivation = models.BooleanField(default=False)
balance = models.IntegerField(default=0)
objects = UserManager()
USERNAME_FIELD = 'username'
class Subscription(models.Model):
subID = models.AutoField(primary_key=True)
nameOf = models.CharField(max_length=50)
price = models.PositiveIntegerField()
salePercentage = models.PositiveIntegerField(default=0)
saleExpiration = models.DateTimeField(default=datetime.datetime.now, blank=True)
def __str__(self):
return f"{self.nameOf}"
class SubPurchase(models.Model):
price = models.PositiveIntegerField()
dateOf = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
subscription = models.ForeignKey(Subscription, null=True, on_delete=models.SET_NULL)
def __str__(self):
return self.subscription
# serializers.py
class UserInfoSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
fields = ('userID', 'username','email', 'name', 'balance', 'emailActivation', 'isSuspended')
read_only_fields = ('userID', 'username','email', 'name', 'balance', 'emailActivation', 'isSuspended')
# views.py
class UserInfoViewSet(viewsets.ModelViewSet):
queryset = get_user_model().objects.all()
serializer_class = UserInfoSerializer
def get_queryset(self):
uID = getattr(self.request.user,'userID')
return get_user_model().objects.filter(userID=uID)
def get_object(self):
uID = getattr(self.request.user,'userID')
return self.queryset.filter(userID=uID)
Again, I need to change the UserInfoSerializer in a way that would give me the user's current subscription's name, ID and expiration date which would be 30 days after the purchase date
If you are only interested in the returned data, you can override the function to_representation of your serializer and create a serializer for your related model. If I understood correctly, the current subscription of your user is the last one (if sorted by "dateOf"). So something like that could do the trick
class SubscriptionSerializer(serializers.ModelSerializer):
class Meta:
model = Subscription
fields = ('nameOf', 'id', 'saleExpiration ')
class UserInfoSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
fields = ('userID', 'username','email', 'name', 'balance', 'emailActivation', 'isSuspended')
read_only_fields = ('userID', 'username','email', 'name', 'balance', 'emailActivation', 'isSuspended')
def to_representation(self, instance):
data = super().to_representation(instance)
current_subs = instance.subpurchase_set.order_by('dateOf').last().subscription
data['current_subscription'] = SubscriptionSerializer(instance=current_subs).data
return data
you can use NestedSerializers to achieve what you are looking for
basically, nested serialization is a method in which you can return, create, put..., into a model from another model, it goes like this..
models.py
class User(AbstractBaseUser, PermissionsMixin):
....
#user model data
class SubPurchase(models.Model):
...
user = models.ForeignKey(User, null=True, blank=True, on_delete=models.CASCADE)
serializers.py
class SubscriptionSerializer(serializers.ModelSerializer):
class Meta:
model = Subscription
fields =["anyfield you wanna include"]
class SubPurchaseSerializer(serializers.ModelSerializer):
class Meta:
model = SubPurchase
fields =["anyfield you wanna include"]
class UserInfoSerializer(serializers.ModelSerializer):
subpurchace = SubPurchaseSerializer()
subscription= SubscriptionSerializer() #later included in the fields of this serializer
class Meta:
model = get_user_model()
fields = ('userID','subpurchace', 'subscription', 'username','email', 'name', 'balance', 'emailActivation', 'isSuspended')
read_only_fields = ('userID', 'username','email', 'name', 'balance', 'emailActivation', 'isSuspended')
I have two models in my project: Author and Book. Each book has a foreignkey that points to the author of the book.
I want to write an api which retrieves and instance of an Author and shows the details of that specific person.
The problem is that I don't know how to include that said person's books in my API.
This is my models.py:
class Book(models.Model):
title = models.CharField(max_length=150)
rating = models.IntegerField(default=0, validators=[MaxValueValidator(10), MinValueValidator(0),])
summary = models.TextField()
author = models.ForeignKey(Author, null=True, on_delete=models.SET_NULL)
class Author(models.Model):
authorID = models.AutoField(primary_key=True)
name = models.CharField(max_length=200)
dateOfBirth = models.DateField(null=True)
nationality = models.CharField(null=True, max_length=255)
AND this is the method that didn't work for me:
# Serializers.py
class AuthorRetrieveSerializer(serializers.ModelSerializer):
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
bookDetails = BookSerializer(read_only=True, many=True)
class Meta:
model = Author
fields = ('name', 'dateOfBirth', 'nationality', 'bookDetails')
# Views.py
class AuthorRetrieveViewSet(RetrieveUpdateDestroyAPIView):
permission_classes = (AllowAny,)
serializer_class = serializers.AuthorRetrieveSerializer
queryset = Author.objects.all()
lookup_field = 'authorID'
def get_queryset(self):
return self.queryset
This code retrieves the Author details successfully but doesn't give me their Books.
Have you tried specifying the source on the serializer?
# Serializers.py
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
class AuthorRetrieveSerializer(serializers.ModelSerializer):
bookDetails = BookSerializer(read_only=True, many=True, source="book_set")#correction here
class Meta:
model = Author
fields = ('name', 'dateOfBirth', 'nationality', 'bookDetails')
I have two models Category & Post. In Post model there is foreign key of category. Based on category I want to filter the data to show the post category wise. Here's my code.
models.py
class Category(models.Model):
name = models.CharField(max_length=200)
slug = models.SlugField()
parent = models.ForeignKey('self',blank=True, null=True ,related_name='news', on_delete=models.CASCADE)
class Meta:
unique_together = ('slug', 'parent',)
verbose_name_plural = "Category"
def __str__(self):
full_path = [self.name]
k = self.parent
while k is not None:
full_path.append(k.name)
k = k.parent
return ' -> '.join(full_path[::-1])
class Post(models.Model):
NEWS_TYPE = (('Images','Images'),('Multi-Images','Multi-Images'),('Image-Text','Image-Text'),
('Audio-Video','Audio-Video'),('Audio-Video-Text','Audio-Video-Text'),('Audio','Audio'),
('Audio-Text','Audio-Text'))
POST_STATUS = (('Pending','Pending'),('Verified','Verified'),('Un-Verified','Un-Verified'),
('Published','Published'),('Mint','Mint'))
category = models.ForeignKey(Category, related_name='posts', on_delete=models.CASCADE)
post_type = models.CharField(max_length=100, verbose_name='Post Type', choices=NEWS_TYPE)
title = models.TextField(verbose_name='News Title')
content = models.TextField(verbose_name='News Content')
hash_tags = models.CharField(max_length=255, verbose_name='Hash Tags')
source = models.CharField(max_length=255, verbose_name='News Source')
author = models.ForeignKey(User, related_name='Post', on_delete=models.CASCADE)
views = models.ManyToManyField(User,related_name='Views', blank=True)
likes = models.ManyToManyField(User, related_name='Likes', blank=True)
dislikes = models.ManyToManyField(User, related_name='Dislikes', blank=True)
status = models.CharField(max_length=20, verbose_name='Status', choices=POST_STATUS, default='Pending')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return (self.post_type)+ '-' +self.title
serializers.py
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = '__all__'
class PostSerializer(serializers.ModelSerializer):
category = CategorySerializer(many=True, read_only=True)
class Meta:
model = Post
fields = ('category','post_type','title','content','hash_tags','source','author','views',
'likes','dislikes','status')
views.py
class CategoryAPI(viewsets.ModelViewSet):
queryset = Category.objects.all()
serializer_class = CategorySerializer
class PostAPI(viewsets.ModelViewSet):
serializer_class = PostSerializer
def get_queryset(self):
news_post = Post.objects.all()
return news_post
def retrieve(self, request, *args, **kwargs):
params = kwargs
print(params['pk'])
category = Category.objects.filter(name=params['pk'])
serializer = CategorySerializer(category, many=True)
return Response(serializer.data)
urls.py
from django.urls import path, include
from rest_framework import routers
from rest_framework.routers import DefaultRouter
from news.views import PostAPI, CategoryAPI
from . import views
router = DefaultRouter()
router.register('posts', views.PostAPI, basename='posts'),
router.register('category', views.CategoryAPI, basename='category'),
urlpatterns = router.urls
I tried solving in these way but it tells 'PostSerializer' object has no attribute 'get_category'. Is there anything i'm doing wrong. Please your support would be helpful. Thank you
I think then your approach should be the other way round, meaning you should add the list of Posts to your Category:
serializers.py
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ('category','post_type','title','content','hash_tags','source','author','views',
'likes','dislikes','status')
class CategorySerializer(serializers.ModelSerializer):
posts = PostSerializer(many=True, read_only=True)
class Meta:
model = Category
fields = ['name', 'slug', 'parent', 'posts']
Attention: I changed the related name of your category field in the Post model to 'posts'
This should show you all Posts when retrieving a category. No need to override any method in your views:
class CategoryAPI(viewsets.ModelViewSet):
queryset = Category.objects.all()
serializer_class = CategorySerializer
class PostAPI(viewsets.ModelViewSet):
queryset = Post.obejcts.all()
serializer_class = PostSerializer
If do not want identify the category by id but by category name, e.g.:
http://127.0.0.1:8000/news/category/sports/
add a custom lookup field to your category view, e.g.
class CategoryAPI(viewsets.ModelViewSet):
queryset = Category.objects.all()
serializer_class = CategorySerializer
lookup_field = 'name'
but make sure the lookup_field is unique
Trying to add 3rd nested serializer using django rest framework
how to add 3rd nested realation in given code -
models.py
class Category(models.Model):
cate_id = models.AutoField(primary_key=True)
cate_name = models.CharField(max_length=45)
class Meta:
managed = False
db_table = 'category'
class SubCategory(models.Model):
sub_cate_id = models.AutoField(primary_key=True)
sub_cate_name = models.CharField(max_length=45)
sub_fk = models.ForeignKey('Category', models.CASCADE, db_column='sub_fk')
class Meta:
managed = False
db_table = 'sub_category'
class Products(models.Model):
pro_id = models.AutoField(primary_key=True)
pro_name = models.CharField(max_length=45)
description = models.CharField(max_length=45, blank=True, null=True)
price = models.IntegerField()
quantity = models.IntegerField()
pro_cate_fk = models.ForeignKey('Category', models.CASCADE, db_column='pro_cate_fk')
pro_sub_fk = models.ForeignKey('SubCategory', models.CASCADE, db_column='pro_sub_fk')
image = models.CharField(max_length=205)
class Meta:
managed = False
db_table = 'products'
from rest_framework import serializers
from .models import Category,SubCategory,Products
class ProductsSerializer(serializers.ModelSerializer):
# x= ChildTable.objects.all().values
class Meta:
model = Products
fields = ('pro_id','pro_name','description','price','quantity','image')
class SubCategorySerializer(ProductsSerializer):
products_set = ProductsSerializer(many=True, read_only=True)
class Meta:
model = SubCategory
fields = ('sub_cate_name','sub_cate_id','products_set')
class CategorySerializer(SubCategorySerializer):
subcategory_set = ProductsSerializer(many=True, read_only=True,)
# pro_subcate_set = SubCategorySerializer(many=True, read_only=True)
class Meta:
model = Category
fields = ('cate_name','cate_id','subcategory_set')
Got this error while attempting -
Got AttributeError when attempting to get a value for field `pro_name` on serializer `ProductsSerializer`. The serializer field might be named incorrectly and not match any attribute or key on the `SubCategory` instance. Original exception text was: 'SubCategory' object has no attribute 'pro_name'.
Is it possible to connect 2 already connected serializer to another serializer?
Make a test and see if it works.
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ('cate_id', 'cate_name')
class SubCategorySerializer(serializers.ModelSerializer):
class Meta:
model = SubCategory
fields = ('sub_cate_id', 'sub_cate_name', 'sub_fk')
def to_representation(self, instance):
response = super().to_represntation(instance)
response['sub_fk'] = CategorySerializer(instance.sub_fk).data
return response
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Products
fields = ('pro_id', 'pro_name', 'description', 'price', 'quantity', 'pro_cate_fk', 'pro_sub_fk', 'image')
def to_representation(self, instance):
response = super().to_representation(instance)
response['pro_cate_fk'] = CategorySerializer(instance.pro_cate_fk).data
response['pro_sub_fk'] = ProductSerializer(instance.pro_sub_fk).data
return response
I am creating sample-api which have posts and followers. Post should visible to followers only
My models.py
from django.contrib.auth.models import User
class Post(models.Model):
creator = models.ForeignKey(User, related_name='creator_post_set', null=True, on_delete=models.CASCADE)
title = models.CharField(max_length=25)
created_date = models.DateTimeField(auto_now_add=True)
content = models.TextField()
likes = models.BigIntegerField(null=True)
comments = models.BigIntegerField(null=True)
class Follow(models.Model):
follower = models.ForeignKey(User, related_name='following', null=True, on_delete=models.CASCADE)
followed_on = models.DateTimeField(auto_now_add=True)
following = models.ForeignKey(User, related_name='follower',null=True, on_delete=models.CASCADE)
My serializers.py for the models:
class UserSerializer(ModelSerializer):
password = serializers.CharField(write_only=True)
def create(self, validated_data):
user = User.objects.create_user(
username=validated_data['username'],
password=validated_data['password'],
first_name=validated_data['first_name'],
last_name=validated_data['last_name'],
)
return user
class Meta:
model = User
fields = ('password', 'username', 'first_name', 'last_name',)
class PostListSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['title', 'content', 'created_date',]
class FollowSerializer(serializers.ModelSerializer):
class Meta:
model = Follow
fields = '__all__'
My views.py:
class PostList(generics.ListCreateAPIView):
serializer_class = PostListSerializer
follow_model = FollowSerializer.Meta.model
post_model = PostSerializer.Meta.model
def get_queryset(self):
try:
followers = self.follow_model.objects.get(follower_id =
self.request.user.id)
queryset = self.post_model.objects.get(creator__in = followers)
except self.follow_model.DoesNotExist:
queryset = None
return queryset
When I call this view it returns the following error:
Cannot query "Follow object (1)": Must be "User" instance.
I need help Thanks in Advance.
As I can see, Post model's creator is FKed to User model. So you need to query using User model instance, not Follower model.
You can use the following code:
following = self.request.user.following.all().values_list('follower', flat=True) # because of related name
queryset = self.post_model.objects.filter(creator_id__in = list(following))
Here I have first retrieved the user ids using self.request.following.all() by reverse relationship. Then I have extracted the user ids using values_list. After that, I have used it in Post.objects.filter(...) method.