I've method in admin.py
def save_model(self, request, instance, form, change):
user = request.user
instance = form.save(commit=False)
if not change or not instance.created_by:
instance.created_by = user
instance.save()
form.save_m2m()
if instance.send_to_killer:
serializer = OrderSerializer(
instance)
emailClass = Email()
emailClass.sendemail(serializer, 1, instance)
return instance
and other one is
class Email(object):
def sendemail(self, data, sendfrom, instance):
print(instance.id)
orders_item = order_item.objects.filter(order_id=instance.id)
print(orders_item)
for item in orders_item:
print(item.qty)
I received instance.id but not getting orders_item as items are saved after order. so how i can get order items data while saving m2m ??
models are
class Order(models.Model):
date = models.DateField(default=timezone.now)
project = models.ForeignKey(
Project, on_delete=models.CASCADE, limit_choices_to={'is_active': True})
send_to_killer = models.BooleanField(default=False)
send_to_sigler = models.BooleanField(default=False)
created_at = models.DateTimeField(default=timezone.now)
created_by = models.ForeignKey(User, on_delete=models.CASCADE, blank=True)
class order_item(models.Model):
# When a Event is deleted, upload models are also deleted
order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name="order_item",
related_query_name="order_item")
item = models.ForeignKey(
Item, on_delete=models.CASCADE, limit_choices_to={'is_active': True}, blank=True, default="")
qty = models.IntegerField(default=0, blank=True)
price = models.DecimalField(
default=Decimal(0), max_digits=12, decimal_places=2, blank=True)
and serializers of order and order_itemare
class orderItemSerializer(serializers.ModelSerializer):
class Meta:
model = order_item
fields = ['qty', 'price']
class OrderSerializer(serializers.ModelSerializer):
order_items = orderItemSerializer(many=True, read_only=True)
class Meta:
model = Order
fields = ['id', 'date', 'project', 'order_items']
You can do it using a signal.
from django.db.models.signals import m2m_changed
m2m_changed.connect(sendemail, sender=YourTable.YourTable_many_to_many_attribute.through)
For detail, please share your model
Related
I have a custom user class and a profile class. Profile class has a OneToOne relation with the custom User. the Serializer is having User as Meta model with adding Profile model in a new field profile extended to the fields tuple. but When I try to get the detail view it returns an error saying Profile field is not an attribute of CustomUser.
I would appreciate if you go over the code that I added below and help me through this.
The User model:
class CustomUser(AbstractBaseUser, PermissionsMixin):
class Types(models.TextChoices):
DOCTOR = "DOCTOR", "Doctor"
PATIENT = "PATIENT", "Patient"
# what type of user
type = models.CharField(_("Type"), max_length=50, choices=Types.choices, null=True, blank=False)
avatar = models.ImageField(upload_to="avatars/", null=True, blank=True)
email = models.EmailField(max_length=255, unique=True)
name = models.CharField(max_length=255)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
objects = CustomBaseUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['name', 'type'] #email is required by default
def get_full_name(self):
return self.name
def __str__(self):
return self.email
The Profile Model:
class DoctorProfile(models.Model):
"""Model for Doctors profile"""
class DoctorType(models.TextChoices):
"""Doctor will choose profession category from enum"""
PSYCHIATRIST = "PSYCHIATRIST", "Psychiatrist"
PSYCHOLOGIST = "PSYCHOLOGIST", "Psychologist"
DERMATOLOGIST = "DERMATOLOGIST", "Dermatologist"
SEXUAL_HEALTH = "SEXUAL HEALTH", "Sexual health"
GYNECOLOGIST = "GYNECOLOGIST", "Gynecologist"
INTERNAL_MEDICINE = "INTERNAL MEDICINE", "Internal medicine"
DEVELOPMENTAL_THERAPIST = "DEVELOPMENTAL THERAPIST", "Developmental therapist"
owner = models.OneToOneField(CustomUser, on_delete=models.CASCADE, related_name='doctor_profile')
doctor_type = models.CharField(
_("Profession Type"),
max_length=70,
choices=DoctorType.choices,
null=True,
blank=False)
title = models.IntegerField(_('Title'), default=1, choices=TITLES)
date_of_birth = models.DateField(null=True, blank=False)
gender = models.IntegerField(_('Gender'), default=1, choices=GENDERS)
registration_number = models.IntegerField(_('Registration Number'), null=True, blank=False)
city = models.CharField(_('City'), max_length=255, null=True, blank=True)
country = models.CharField(_('Country'), max_length=255, null=True, blank=True)
def __str__(self):
return f'profile-{self.id}-{self.title} {self.owner.get_full_name()}'
Serializer:
class DoctorProfileFields(serializers.ModelSerializer):
"""To get the fields from the DoctorProfile. it will be used in the DoctorProfileSerializer"""
class Meta:
model = DoctorProfile
fields = ('doctor_type', 'title', 'date_of_birth', 'registration_number', 'gender', 'city', 'country', )
class DoctorProfileSerializer(serializers.ModelSerializer):
"""retrieve, update and delete profile"""
profile = DoctorProfileFields()
class Meta:
model = User
fields = ('name', 'avatar', 'profile', )
#transaction.atomic
def update(self, instance, validated_data):
ModelClass = self.Meta.model
profile = validated_data.pop('profile', {})
ModelClass.objects.filter(id=instance.id).update(**validated_data)
if profile:
DoctorProfile.objects.filter(owner=instance).update(**profile)
new_instance = ModelClass.objects.get(id = instance.id)
return new_instance
View:
class DoctorProfileAPIView(generics.RetrieveUpdateDestroyAPIView):
"""To get the doctor profile fields and update and delete"""
serializer_class = DoctorProfileSerializer
queryset = User.objects.all()
def get_object(self):
return get_object_or_404(User, id=self.request.user.id, is_active=True)
What I want is a json response in the detail view like below:
{
"name": the name,
"avatar": avatar,
"profile": {
"doctor_type": "PSYCHIATRIST",
"title": 1,
"date_of_birth": 11-11-1990,
"registration_number": 21547,
}
}
Can Anybody guide me through this..? Or is there any other design approach that meets my objective. My objective is to have the user info + profile info combined in a single endpoint as a whole Profile in the frontend from which the user will see/edit profile.
First of all move the foreign key OneToOne in the CustomUser model, add:
owner = models.OneToOneField('DoctorProfile', on_delete=models.CASCADE, related_name='doctor_profile')
and delete from DoctorProfile:
owner = models.OneToOneField(CustomUser, on_delete=models.CASCADE, related_name='doctor_profile')
Make all migrations, and now you have to set new data in the db.
In the serializers you are using Nested relationships correctly, so add the attribute many set to False:
profile = DoctorProfileFields(many=False)
Edit
If you cant edit the structure of your models, you can work with SerializerMethodField (not tested):
class DoctorProfileSerializer(serializers.ModelSerializer):
class Meta:
model = DoctorProfile
fields = ('doctor_type', 'title', 'date_of_birth', 'registration_number')
class CustomDoctorProfileSerializer(serializers.Serializer):
name = serializers.SerializerMethodField()
avatar = serializers.SerializerMethodField()
profile = DoctorProfileSerializer(many=False)
def get_name(self, obj)
return obj.doctor_profile.name
def get_avatar(self, obj)
return obj.doctor_profile.avatar
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)
I am trying to update Customer Profile also updating main Customuser first_name and last_name field at the same time using nested serialization. But I am getting customer field must be unique error.
I have posted the pics here.
My models:
class CustomUser(AbstractUser):
# username = None
first_name = models.CharField(max_length=255, verbose_name="First name")
last_name = models.CharField(max_length=255, verbose_name="Last name")
email = models.EmailField(unique=True)
is_seller = models.BooleanField(default=False)
is_customer = models.BooleanField(default=False)
USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["first_name", "last_name"]
objects = CustomUserManager()
def __str__(self):
return self.email
class Customer(models.Model):
customer = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, blank=True, null=True)
full_name = models.CharField(max_length=100, blank=True)
phone_num = models.CharField(max_length=50, blank=True)
#dob = models.CharField(max_length=255,blank=True,null=True)
region = models.CharField(max_length=255, blank=True,null=True)
city = models.CharField(max_length=255, blank=True, null=True)
area = models.CharField(max_length=255,blank=True,null=True)
address = models.CharField(max_length=255, blank=True, null=True)
def __str__(self):
return self.customer.email
My serializers:
class CustomerProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Customer
fields = '__all__'
# depth = 1
class CustomerUpdateSerializer(serializers.ModelSerializer):
customer = CustomerProfileSerializer()
class Meta:
model = User
fields = ('id', "first_name", "last_name",'customer')
def update(self,request, instance, validated_data):
user = self.request.user
instance.user.first_name=user.get('first_name')
instance.user.last_name = user.get('last_name')
instance.user.save()
customer_data = validated_data.pop('customer',None)
if customer_data is not None:
instance.customer.region = customer_data['region']
instance.customer.city = customer_data['city']
instance.customer.area = customer_data['area']
instance.customer.address = customer_data['address']
instance.customer.save()
return super().update(instance,validated_data)
My views:
class CustomerUpdateView(UpdateAPIView):
permission_classes = [IsAuthenticated]
queryset = User.objects.all()
serializer_class = CustomerUpdateSerializer
The url is visible in postman put request. I have sent the raw json data in the postman, but it throws this error. How to update those 4 fields in customer and 2 fields (first_name and last_name) from User model??
My code worked after I made some changes to the serializer.
Here is the working code:
class CustomerUpdateSerializer(serializers.ModelSerializer):
customer = CustomerProfileSerializer(many=False)
class Meta:
model = User
fields = ('id', "first_name", "last_name",'customer')
depth = 1
def update(self, instance, validated_data):
user = self.context['request'].user
user.first_name = validated_data.get('first_name')
user.last_name = validated_data.get('last_name')
user.save()
customer_data = validated_data.pop('customer',None)
if customer_data is not None:
instance.customer.region = customer_data['region']
instance.customer.city = customer_data['city']
instance.customer.area = customer_data['area']
instance.customer.address = customer_data['address']
instance.customer.save()
return super().update(instance,validated_data)
Its because I was using instance of user and same instance to customer model. Now, I get the fields first_name and last_name separately and use instance for customer only.It worked.
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 create user due to nested serializer. My question is that how I can create 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
Seralizers.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']
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]
I know there is .create() method which can be override according to DRF documentation. Plus I want to override this method in views.py. Is there any approach to do this. Thanks in advance for your addition in my knowledge.
There are two ways of doing that in my knowledge.
The first one is overriding the create method of the generic view. Which is as follows:
class UserList(generics.ListCreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
def create(self, request, *args, **kwargs):
#let django create the user with generic view
response = super().create(request, *args, **kwargs)
# response.data contains the serialized data of the created user
user_data = response.data
user_id = response.data.get("id")
profile = UserProfile()
profile.user = User.objects.get(id=user_id)
profile.save()
# return the response created by the generic view
return response
Be careful that Django Rest Framework does not support the create method for nested serializers. Set the profile field to read-only in UserSerializer
class UserSerializer(serializers.ModelSerializer):
profile = SerializerMethodField()
class Meta:
model = User
fields = ['id', 'username', 'email', 'first_name', 'last_name', 'profile']
read_only_fields = ('profile',)
def get_profile(self, user):
profile = UserProfile.objects.filter(user=user).first()
return UserProfileSerializer(profile).data if profile is not None else None
The second way is using signals. Signals is an event dispatcher that keeps track of events such as update, delete, m2m changed, etc. Here, we can use post_save signals which trigger whenever a model gets updated. So if the User model is created, we will receive a signal and therefore crate a UserProfile for that user. You can learn more about signals from the Django Signals page.
#receiver(post_save, sender=User, dispatch_uid="create_user_profile")
def on_user_create(sender, instance, created, **kwargs):
# here instance is the user object that just got created
# created is a boolean value which will be true if the record is newly created
if created:
profile = UserProfile()
profile.user = instance
profile.save()
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]