I am new to DRF and I want to do something similar to the formsets in django forms
I have an Invoice And Products models related to each other throw a many to many InvoiceDetail model.. when I create an Invoice I choose some products and create a InvoiceDetail object for each .. I want to do this in DRF how can I serialize the Invoice model and it's create function then?
or should i do it form the view?
models.py:
class Invoices(models.Model):
#some fields
products = models.ManyToManyField('Products', through='InvoiceDetail')
class Products(models.Model):
#some fields
class InvoiceDetail(models.Model):
invoice = models.ForeignKey(Invoices, related_name='parent_invoice')
product = models.ForeignKey(Products, related_name='parent_product')
product_description = models.TextField()
product_price = models.DecimalField(max_digits=9, decimal_places=2)
quantity_sold = models.IntegerField()
serializers.py:
class ProductsSerializer(serializers.ModelSerializer):
class Meta:
model = Products
fields = ('barcode', 'product_code', 'name', 'description', 'category',
'quantity_in_stock', 'quantity_on_hold', 'expire_date',
'vendor', 'manufacturer', 'discount')
class InvoiceDetailsSerializer(serializers.ModelSerializer):
class Meta:
model = InvoiceDetail
fields = '__all__'
view.py:
class ProductsView(viewsets.ReadOnlyModelViewSet):
queryset = Products.objects
serializer_class = ProductsSerializer
class InvoicesView(viewsets.ModelViewSet):
queryset = Invoices.objects
serializer_class = InvoicesSerializer
class InvoiceDetailView(viewsets.ModelViewSet):
queryset = InvoiceDetail.objects
serializer_class = InvoiceDetailsSerializer
You could do this in the serializer itself,
class InvoiceSerializer(serializers.ModelSerializer):
products = serializers.PrimaryKeyRelatedField(queryset=Product.objects.all(), many=True)
class Meta:
model = Invoice
fields = [ f.name for f in model._meta.fields ] + ['products']
def create(self, validated_data):
products = validated_data.pop('products')
invoice = super(InvoiceSerializer, self).create(validated_data)
for product in products:
InvoiceDetail.objects.create(invoice=invoice, product=product)
return invoice
This, is just a basic example for to know about how this works. You could customise it however you need.
Related
I have 2 models ( Users and Posts )
class Users(models.Model):
email = models.CharField(max_length=225)
class Posts(models.Model):
user = models.ForeignKey(Users, on_delete=models.CASCADE, default=1)
type = models.TextField()
I want to include the user email when getting all posts.
I have done the following but am only getting the user id.
class PostsViewSet(viewsets.ModelViewSet):
serializer_class = PostsSerializer
def get_queryset(self):
queryset = Posts.objects.all()
return queryset
How can I achieve to get the user email within the queryset ?
use https://docs.djangoproject.com/en/4.1/ref/models/querysets/#prefetch-related (not strictly needed, but a good habit) to grab it from the database, use post.user.email to grab the value in code. i.e:
class PostsViewSet(viewsets.ModelViewSet):
serializer_class = PostsSerializer
queryset = Posts.objects.all().prefetch_related("user")
class PostsSerializer(serializers.ModelSerializer):
email = serializers.EmailField(source="user.email")
...
You can annotate the field in the queryset:
from django.db.models import F
class PostsViewSet(viewsets.ModelViewSet):
serializer_class = PostsSerializer
def get_queryset(self):
queryset = Posts.objects.annotate(user_email=F('user__email'))
return queryset
Use all power of serializers. https://www.django-rest-framework.org/api-guide/serializers/
class PostsViewSet(viewsets.ModelViewSet):
serializer_class = PostsSerializer
queryset = Posts.objects.all().prefetch_related("user")
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('email')
class PostsSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = Post
fields = ('type', 'user')
depth = 1
I have a comment field for every blog post. I want to pass Comment.objects.all() from Views.py to ModelSerializer def get_comments(self, obj) to reduce the number of sql queries. As I am serializing a list of blog posts
Views.py
class BlogViewSet(ModelViewSet):
queryset = Blog.objects.all().annotate(
author_name=F('author__username')
)
serializer_class = BlogSerializer
permission_classes = [IsOwnerOrReadOnly]
def list(self, request):
return Response({'blogs': BlogSerializer(self.queryset, many=True).data})
Serializers.py
class BlogSerializer(ModelSerializer):
author_name = serializers.CharField(read_only=True)
comments = SerializerMethodField()
class Meta:
model = Blog
fields = ('title_text', 'main_text', 'datepublished', 'author_name', 'id', 'comments')
def get_comments(self, obj):
# filter comment
comment_object = Comment.objects.filter(post_id=obj.id)
comments = CommentSerializer(comment_object, many=True).data
return comments
You don't have to pass anything from the View.
First, you have to change the comments field in your BlogSerializer.
class CommentSerializer(ModelSerializer):
# This serializer should have all the details of your comments.
....
class Meta:
model = Comment
fields = "__all__" # Or whatever fields you want to set.
class BlogSerializer(ModelSerializer):
author_name = serializers.CharField(read_only=True)
comments = CommentSerializer(many=True, read_only=True) # I am not sure of the source of your comment reverse manager name
class Meta:
model = Blog
fields = ('title_text', 'main_text', 'datepublished', 'author_name', 'id', 'comments')
Second, you have to make a small change to your view's queryset in order to reduce the number of queries sent to the database by using prefetch_related
class BlogViewSet(ModelViewSet):
queryset = Blog.objects.prefetch_related('comments').all().annotate(
author_name=F('author__username')
)
serializer_class = BlogSerializer
permission_classes = [IsOwnerOrReadOnly]
def list(self, request):
return Response({'blogs': BlogSerializer(self.get_queryset(), many=True).data})
I assumed in the code snippets that you didn't set a related_name on your Blog ForeignKey for the Comment model, so by default, its related manager on Blog will be comment_set
Update
Your models should look like this, in order for this solution to work
class Comment(models.Model):
...
blog = models.ForeignKey('Blog', on_delete=models.CASCADE, related_name='comments')
...
class Blog(models.Model):
# comment = models.ForeignKey(Comment, on_delete=models.CASCADE, null=True)
# this foreign key shouldn't be here, remove it.
....
Do the changes on Serializer and View
# in BlogSerializer
comments = CommentSerializer(many=True, read_only=True)
# In BlogViewSet
queryset = Blog.objects.prefetch_related('comments').all().annotate(
author_name=F('author__username')
)
id field and name field not showing in result.
in models.py:
class Group(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=50)
admin = models.ForeignKey(User, on_delete=models.CASCADE)
member = models.ManyToManyField(User, related_name='groups_user')
def __str__(self):
return self.name
in serializers.py:
class SimpleUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id','first_name', 'last_name')
class GroupSerializer(serializers.Serializer):
admin = SimpleUserSerializer()
class Meta:
model = Group
fields = ('id','name','admin')
views.py:
#api_view(['GET'])
#permission_classes((IsAuthenticated,))
def getSomeGroup(request):
allGroup = Group.objects.all().count()
randomGroupId = random.sample(range(allGroup), 3)
randomGroup = Group.objects.filter(id__in=randomGroupId)
serializer = GroupSerializer(randomGroup, many=True)
#print(serializer)
return Response(serializer.data)
the result comes like this:
[{"admin":{"id":1,"first_name":"asif","last_name":""}},{"admin":{"id":3,"first_name":"Test2","last_name":"lastname"}},{"admin":{"id":3,"first_name":"Test2","last_name":"lastname"}}]
why id and name field not showing?
class SimpleUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
First try to access all admin
#api_view(['GET'])
#permission_classes(IsAuthenticated)
def getSomeGroup(request):
randomGroup = Group.objects.all()
serializer = GroupSerializer(randomGroup, many=True)
return Response(serializer.data)
If that works there may be issue in your these two line
The Issue may be in these two lines
allGroup = Group.objects.all().count()
randomGroupId = random.sample(range(allGroup), 3)
Modify serializers.py:
class GroupSerializer(serializers.ModelSerializer):
I'm a Django Rest Framework and Django newbie
i can use random data to make stages but i can't use serializer to add new stages.
My model and serializer
class Stage(models.Model):
class Meta:
db_table = 'stage'
stage_id = models.AutoField(primary_key=True)
stage_name = models.CharField(max_length=64, null=False)
company = models.ForeignKey(
Company,
db_column='id',
on_delete=models.CASCADE,
)
class StageSerializer(ModelSerializer):
stage_id = IntegerField(read_only=True)
class Meta:
model = Stage
fields = [
'stage_id',
'stage_name',
'company',
]
def update(self, instance, validated_data):
pass
def create(self, validated_data):
# create stages
stage = create_stage(**validated_data)
return stage
view.py
class StageListAPIView(APIView):
def post(self, request, company_id):
data = request.data.copy()
company = get_company_by_id(company_id)
data['company'] = company.pk
serializer = StageSerializer(data=data)
if not serializer.is_valid(raise_exception=True):
return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
new_data = serializer.validated_data
serializer.save(company=company)
return Response(new_data, status=HTTP_200_OK)
request.data
<QueryDict: {'stage_name': ['kAkSdKq9Gt'], 'company': [6]}>
i will receive error:
TypeError: Object of type Company is not JSON serializable
i can't understand it and i don't know how to use serializer to save foreign key.
You need to serialize the Company instance before you can include it in your StageSerializer.
A simple example would be something like
class CompanySerializer(ModelSerializer):
class Meta:
model = Company
fields = '__all__'
And then to include that in your StageSerializer:
class StageSerializer(ModelSerializer):
stage_id = IntegerField(read_only=True)
company = CompanySerializer(source='company', read_only=True)
class Meta:
model = Stage
fields = [
'stage_id',
'stage_name',
'company',
]
I am designing an API for listing stores and create store. I could list store but while designing for creating store I am not getting product and store category field inspite of calling all Product and Store category serializer in Store Serializer.
My shortened models look like
class Merchant(models.Model):
user = models.ForeignKey(User)
phone = models.PositiveIntegerField(null=True,blank=True)
class Store(models.Model):
merchant = models.ForeignKey(Merchant)
name_of_legal_entity = models.CharField(max_length=250)
class Product(models.Model):
store = models.ForeignKey(Store)
image = models.ForeignKey('ProductImage',blank=True,null=True)
name_of_product = models.CharField(max_length=120)
class ProductImage(models.Model):
image = models.ImageField(upload_to='products/images/')
class StoreCategory(models.Model):
product = models.ForeignKey(Product,null=True, on_delete=models.CASCADE,related_name="store_category")
store_category = models.CharField(choices=STORE_CATEGORIES, default='GROCERY', max_length=10)
Serializer.py
class ProductImageSerializer(ModelSerializer):
class Meta:
model = ProductImage
fields = ( 'id','imageName', )
class ProductSerializers(ModelSerializer):
image = ProductImageSerializer(many=False,read_only=True)
class Meta:
model = Product
fields=('id','image','name_of_product','description','price','active',)
class StoreCategorySerializer(ModelSerializer):
product = ProductSerializers(read_only=True)
class Meta:
model = StoreCategory
class StoreSerializer(ModelSerializer):
# url = HyperlinkedIdentityField(view_name='stores_detail_api')
store_categories = StoreCategorySerializer(many=True)
merchant = MerchantSerializer(read_only=True)
class Meta:
model = Store
fields=("id",
"merchant",
"store_categories",
"name_of_legal_entity",
"pan_number",
"registered_office_address",
"name_of_store",
)
Views.py
class StoreCreateAPIView(CreateAPIView):
queryset = Store.objects.all()
serializer_class = StoreSerializer
parser_classes = (FormParser,MultiPartParser,)
def put(self, request, filename, format=None):
print('first put works')
file_obj = request.data['file']
print ('file_obj',file_obj)
return Response(status=204)
def perform_create(self, serializer):
print('then perform works')
serializer.save(user=self.request.user)
Here is the screenshot of how it looks
Why it is not showing Merchant, Product and Store Category in the form?
Remove read_only=True from the serializers that you wanna create entry of.
Like:
product = ProductSerializers(read_only=True)
should be
product = ProductSerializers()
read_only will prevent it from written therefore i wont be in the outcome.