Django select_related doesn't optimize query - django

I have a problem with select_related. I don't know what I'm doing wrong but it doesn't work..
models.py
class OrganizerUser(models.Model):
"""This user manage Agents"""
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
def __str__(self):
return self.user.username
class Agent(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
organizer = models.ForeignKey(OrganizerUser, blank=True, null=True,
on_delete=models.CASCADE)
def __str__(self):
return self.user.username
class Lead(models.Model):
first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
age = models.IntegerField(default=0)
organizer = models.ForeignKey(OrganizerUser, on_delete=models.CASCADE)
agent = models.ForeignKey(Agent, null=True, blank=True, on_delete=models.SET_NULL)
category = models.ForeignKey(
Category, related_name="categories", null=True, blank=True, on_delete=models.SET_NULL
)
description = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
phone_number = models.CharField(max_length=20)
email = models.EmailField()
converted_date = models.DateTimeField(null=True, blank=True)
def __str__(self):
return f"{self.first_name} {self.last_name}"
views.py
class LeadsApiView(generics.ListCreateAPIView):
serializer_class = LeadSerializer
permission_classes = [IsAuthenticated, IsAdminOrOrganizer]
def get_queryset(self):
user = self.request.user
#if user.is_staff:
#return Lead.objects.select_related('organizer', 'agent').all()
if user.is_organizer:
return Lead.objects.select_related('organizer').filter(
organizer=user.organizeruser)
else:
return Lead.objects.select_related('agent').filter(agent=user.agent)
serializers.py
class LeadSerializer(serializers.ModelSerializer):
class Meta:
model = Lead
fields = ['id', 'first_name', 'last_name', 'age',
'organizer', 'agent', 'category', 'description', 'date_added',
'phone_number', 'email', 'converted_date'
]
for agents everything is fine. Django makes 3 queries but for other users, it makes extra queries for each existing user.

This solution solved the problem
How to always prefetch_related for a specific django model
class UsersManager(models.Manager):
def get_queryset(self):
return super().get_queryset().select_related('user')
class OrganizerUser(models.Model):
"""This user manage Agents"""
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
objects = UsersManager()
def __str__(self):
return self.user.username
class Agent(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
organizer = models.ForeignKey(OrganizerUser, blank=True, null=True,
on_delete=models.CASCADE)
objects = UsersManager()
def __str__(self):
return self.user.username

Related

how to use django Serializer or signal to add object in database?

I create ticket , ticketflow , ticketstate , tickettype models
i need a serializer or signal that when user create ticket programmatically add ticketflow object and set state of ticket to submited or something else
Here is my models
class TicketType(models.Model):
title = models.CharField(max_length=255, blank=False, unique=True, null=False)
def __str__(self):
return self.title
class TicketState(models.Model):
title = models.CharField(max_length=255, blank=False, unique=True, null=False)
def __str__(self):
return self.title
class Ticket(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4 , editable=False)
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete= models.CASCADE)
ticket_type = models.ForeignKey(TicketType,on_delete=models.CASCADE , default=1)
title = models.CharField(max_length=255, blank=False, null=False)
message = models.TextField()
attachment = models.FileField(upload_to='uploads/tickets/', validators=[FileExtensionValidator(allowed_extensions=['pdf','docx','zip','jpg','png'])], blank=True)
created_on = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-created_on']
def __str__(self):
return self.title
class TicketFlow(models.Model):
uuid = models.UUIDField(default=uuid4, editable=False)
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete= models.CASCADE)
ticket = models.ForeignKey(Ticket,on_delete=models.CASCADE , related_name='ticketflow')
ticket_state = models.ForeignKey(TicketState,on_delete=models.CASCADE , default=1 , related_name='ticketstate')
message = models.TextField()
attachment = models.FileField(upload_to='uploads/tickets/', validators=[FileExtensionValidator(allowed_extensions=['pdf','docx','zip','jpg','png'])], blank=True)
created_on = models.DateTimeField(auto_now_add=True)
updated_on = models.DateTimeField(auto_now= True)
class Meta:
ordering = ['-created_on']
here is my serializers
class TicketTypeSerializer(serializers.ModelSerializer):
class Meta:
model = TicketType
fields = ('id','title',)
class TicketStateSerializer(serializers.ModelSerializer):
class Meta:
model = TicketState
fields = ('id','title',)
class TicketSerializer(serializers.ModelSerializer):
class Meta:
model = Ticket
fields = ['id' , 'author', 'ticket_type','title' ,'message' , 'attachment' , 'created_on']
class TicketFlowSerializer(serializers.ModelSerializer):
class Meta:
model = TicketFlow
fields = ['author', 'ticket_state', 'message', 'attachment', 'created_on', 'updated_on']
It'll be great if someone can help me out in this. how can i create signal or override create method in serializers
You probably want your "state" field to be read-only in the serializer, this way it can only be changed programmatically, and in the model set a default value with default='pending'.
Then you can override the update method in a Serializer (see the doc here):
def update(self, instance, validated_data):
validated_data['state'] = 'edited'
return super(MySerializer, self).update(instance, validated_data)

(1048, "Column 'brand_id' cannot be null") in django rest framework

I am trying to create an api where a user can add their products. I am sending raw json data from postman but it is giving this error.
IntegrityError at /api/addproducts
(1048, "Column 'brand_id' cannot be null")
I am sending brand id in the data. I am not sure what is happening.
Here I am sending merchant_id as well as categories ids but why brand field is creating an error I am not sure.
My models:
class Category(models.Model):
name = models.CharField(max_length=100, unique=True)
image = models.ImageField(null=True, blank=True)
class Meta:
verbose_name_plural = "Categories"
def __str__(self):
return self.name
class Brand(models.Model):
brand_category = models.ForeignKey(Category,on_delete=models.CASCADE,blank=True,null=True)
name = models.CharField(max_length=100, unique=True)
image = models.ImageField(null=True, blank=True)
class Meta:
verbose_name_plural = "Brands"
def __str__(self):
return self.name
class Collection(models.Model):
name = models.CharField(max_length=100, unique=True)
image = models.ImageField(null=True, blank=True)
class Meta:
verbose_name_plural = "Collections"
def __str__(self):
return self.name
class Variants(models.Model):
SIZE = (
('not applicable', 'not applicable',),
('S', 'Small',),
('M', 'Medium',),
('L', 'Large',),
('XL', 'Extra Large',),
)
AVAILABILITY = (
('available', 'Available',),
('not_available', 'Not Available',),
)
product_id = models.CharField(max_length=70,default='OAXWRTZ_12C',blank=True)
price = models.DecimalField(decimal_places=2, max_digits=20,default=500)
size = models.CharField(max_length=50, choices=SIZE, default='not applicable',blank=True,null=True)
color = models.CharField(max_length=70, default="not applicable",blank=True,null=True)
variant_image = models.ImageField(upload_to="products/images", blank=True)
thumbnail = ImageSpecField(source='variant_image',
processors=[ResizeToFill(100, 50)],
format='JPEG',
options={'quality': 60})
quantity = models.IntegerField(default=10,blank=True,null=True) # available quantity of given product
variant_availability = models.CharField(max_length=70, choices=AVAILABILITY, default='available')
class Meta:
verbose_name_plural = "Variants"
def __str__(self):
return self.product_id
class Product(models.Model):
merchant = models.ForeignKey(Seller,on_delete=models.CASCADE,blank=True,null=True)
category = models.ManyToManyField(Category, blank=False)
brand = models.ForeignKey(Brand, on_delete=models.CASCADE)
collection = models.ForeignKey(Collection, on_delete=models.CASCADE)
featured = models.BooleanField(default=False) # is product featured?
best_seller = models.BooleanField(default=False)
top_rated = models.BooleanField(default=False)
My serializers:
class BrandSerializer(serializers.ModelSerializer):
# id = serializers.IntegerField()
class Meta:
model = Brand
fields = '__all__'
class AddProductSerializer(serializers.ModelSerializer):
merchant = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = Product
fields = ['id','merchant','category','brand', 'collection','featured', 'top_rated',
'name','description', 'picture','main_product_image','best_seller',
'rating','availability','warranty','services','variants']
# depth = 1
def create(self, validated_data):
product = Product.objects.create()
return product
My views:
class ProductAddAPIView(CreateAPIView):
permission_classes = [AllowAny]
queryset = Product.objects.all()
serializer_class = AddProductSerializer
i think the problem is in the create function in serializer
class AddProductSerializer(serializers.ModelSerializer):
merchant = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = Product
fields = ['id','merchant','category','brand', 'collection','featured', 'top_rated',
'name','description', 'picture','main_product_image','best_seller',
'rating','availability','warranty','services','variants']
# depth = 1
def create(self, validated_data):
product = Product.objects.create()
return product
as you didn't pass the validated data so it create an empty object use this instead
def create(self, validated_data):
return Product.objects.create(**validated_data)

How to update a User and its Profile in nested serializer using generic ListCreateAPIView?

I am working on genericAPIViews in DRF. I am using a built in user model with UserProfile model having one to one relation with it. But I am unable to update user due to nested serializer. My question is that how I can update my built in User model and Profile User model at the same time as UserProfile model is nested in User model.Here is my code:
Models.py
USER_CHOICE = (
('SS', 'SS'),
('SP', 'SP')
)
LOGIN_TYPE = (
('Local', 'Local'),
('Facebook', 'Facebook'),
('Google', 'Google')
)
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
cell_phone = models.CharField(max_length=15, blank=True, default="", null=True)
country = models.CharField(max_length=50, blank=True, default="", null=True)
state = models.CharField(max_length=50, blank=True, default="", null=True)
profile_image = models.FileField(upload_to='user_images/', default='', blank=True)
postal_code = models.CharField(max_length=50, blank=True, default="", null=True)
registration_id = models.CharField(max_length=200, null=True, blank=True, default=None)
active = models.BooleanField(default=True)
# roles = models.ForeignKey(Role, null=True, on_delete=models.CASCADE, related_name='role', blank=True)
user_type = models.CharField(max_length=50, choices=USER_CHOICE, null=True, blank=True)
login_type = models.CharField(max_length=40, choices=LOGIN_TYPE, default='local')
reset_pass = models.BooleanField(default=False)
confirmed_email = models.BooleanField(default=False)
remember_me = models.BooleanField(default=False)
reset_code = models.CharField(max_length=200, null=True, blank=True, default="")
reset_code_time = models.DateTimeField(auto_now_add=True, blank=True)
longitude = models.DecimalField(max_digits=80, decimal_places=10, default=0.00)
latitude = models.DecimalField(max_digits=80, decimal_places=10, default=0.00)
r_code = models.CharField(max_length=15, null=True, blank=True)
refer_user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True, related_name="user_refer")
referred = models.ManyToManyField(User, related_name="user_referred", null=True, blank=True)
otp = models.CharField(max_length=6, blank=True, default="", null=True)
def __str__(self):
return self.user.username
Seralizer.py
from rest_framework import serializers
from django.contrib.auth.models import User
from .models import UserProfile
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = '__all__'
class UserSerializer(serializers.ModelSerializer):
profile = UserProfileSerializer()
class Meta:
model = User
fields = ['id', 'username', 'email', 'first_name', 'last_name', 'profile']
def create(self, validated_data):
profile_data = validated_data.pop('profile')
user = User.objects.create(**validated_data)
Profile.objects.create(user=user, **profile_data)
return user
Views.py
class UserList(generics.ListCreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
class UserDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [IsAdminUser]
How can write my .update() methods which can be override according to DRF documentation.Thanks in advance for your addition to my knowledge.
There is update method in ModelSerializer which can be overriden the same way as you did with create.
def update(self, instance, validated_data):
profile_data = validated_data.pop('profile', {})
profile = instance.profile
for attr, value in profile_data.items():
setattr(profile, attr, value)
profile.save()
return super(UserSerializer, self).update(instance, validated_data)
You can also write nested serializer.

how to deleting extra OrderItems in Orders admin panel?

this is my order admin. when a customer reserve a product it goes to order item section in the down. but there are some extra order items that they are empty. i want to when a customer adds a product just that product be in the admin panel. how i can remove that empty order items???
model:
class Order(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE, related_name='orders')
ref_code = models.CharField(max_length=20, blank=True, null=True)
# products = models.ManyToManyField(OrderItem)
address = models.ForeignKey(Address, on_delete=models.SET_NULL, null=True, blank=True, related_name='addresses')
created_date = models.DateTimeField(auto_now_add=True)
ordered_date = models.DateTimeField(null=True, blank=True)
ordered = models.BooleanField(default=False)
def __str__(self):
return self.user.full_name
class OrderItem(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name='products')
ordered = models.BooleanField(default=False)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
quantity = models.PositiveSmallIntegerField(default=1)
def __str__(self):
return f"{self.quantity} of {self.product.name}"
serializer:
class OrderItemSerializer(serializers.ModelSerializer):
product = serializers.SerializerMethodField()
final_price = serializers.SerializerMethodField()
class Meta:
model = OrderItem
fields = (
'id',
'product',
'quantity',
'final_price'
)
def get_product(self, obj):
return ProductSerializer(obj.product).data
def get_final_price(self, obj):
return obj.get_final_price()
class OrderSerializer(serializers.ModelSerializer):
order_items = serializers.SerializerMethodField()
total = serializers.SerializerMethodField()
class Meta:
model = Order
fields = (
'id',
'order_items',
'total',
)
def get_order_items(self, obj):
return OrderItemSerializer(obj.products.all(), many=True).data
def get_total(self, obj):
return obj.total_price()
You need to change the TabularInline at admin.py file not serializers. extra = 0
class OrderItemInline(admin.TabularInline):
model = OrderItem
extra = 0
#admin.register(Order)
class OrderAdmin(admin.ModelAdmin):
inlines = [OrderItemInline]

KeyError: 'user'

I want to extend my Django user model using rest_framework to create an api endpoint for project, but I'm getting a KeyError: user, below is my code.
profile apiView
class ProfileListView(generics.ListCreateAPIView):
permission_classes = (IsAuthenticatedOrReadOnly,)
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
profile serializer
class ProfileSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(source = 'pk', read_only = True)
username = serializers.CharField(source = 'user.username', read_only = True)
email = serializers.CharField(source = 'user.email', read_only=True)
class Meta:
model = Profile
fields = ('id', 'email', 'username', 'qualification', 'profession', 'phoneNumber',
'city', 'address', 'surname', 'firstName', 'dp', 'yearOfExperience',
'gender'
)
def create(self, validated_data, instance=None):
user_data = validated_data.pop('user')
user = CustomUser.objects._create_user(**validated_data)
Profile.objects.create(user=user, **user)
return user
profile model
class Profile(models.Model):
user = models.OneToOneField(CustomUser, on_delete=models.CASCADE)
firstName = models.CharField(max_length=255, blank=True, null=True)
surname = models.CharField(max_length=255, blank=True, null=True)
yearOfExperience = models.PositiveIntegerField(default=1)
profession = models.CharField(max_length=250, blank=True, null=True)
dp = models.URLField(blank=True, null=True)
qualification = models.CharField(max_length=255, blank=True, null=True)
phoneNumber = models.CharField(max_length=255, blank=True, null=True)
city = models.CharField(max_length=255, blank=True, null=True)
address = models.CharField(max_length=255, blank=True, null=True)
gender = models.CharField(max_length=255, choices=GENDER)
class Meta:
ordering = ('surname', '-firstName', )
verbose_name = 'Profile'
verbose_name_plural = 'Profiles'
def __str__(self):
return self.user.username
#receiver(post_save, sender=CustomUser)
def create_user_profile(sender, instance=None, created=False, **kwargs):
if created:
Profile.objects.create(user=instance)
instance.profile.save()
Error message:
File "/home/olaneat/Desktop/filez/project/django/funzone/lib/python3.7/site-packages
user_data = validated_data.pop('user')
KeyError: 'user'
You need to check if the user is there before accessing it;
def create(self, validated_data, instance=None):
if 'user' in validated_data:
user_data = validated_data.pop('user')
user = CustomUser.objects._create_user(**validated_data)
Profile.objects.create(user=user, **user)
return user
However you don't never need to do that validated_data.pop() because you're not using user_data after assigning it.